diff --git a/.github/agents/issue-fix-agent.agent.md b/.github/agents/issue-fix-agent.agent.md new file mode 100644 index 0000000000..2d12d5b915 --- /dev/null +++ b/.github/agents/issue-fix-agent.agent.md @@ -0,0 +1,5817 @@ +# Copilot Agent Issue Triage & Resolution Plan +## Azure Cosmos DB .NET SDK (azure-cosmos-dotnet-v3) + +--- + +## Quick Start Prompt + +**Copy-paste this prompt to start the Copilot agent workflow:** + +``` +Follow the Copilot Agent Issue Triage Plan in .github/copilot-agent-plan.md + +Guide step-by-step on any required setups and then investigate issues +with label "customer-reported". + +gh issue list --repo Azure/azure-cosmos-dotnet-v3 --label "customer-reported" --state open --limit 5 +``` + +**For a specific issue:** +``` +Follow the Copilot Agent Issue Triage Plan in .github/copilot-agent-plan.md + +Guide step-by-step on any required setups and then investigate issue #XXXX. +``` + +**What the agent will do:** +1. Verify environment setup (gh CLI, .NET SDK, emulator) +2. Fix any setup issues (SAML auth, missing tools) +3. Fetch and triage the issue(s) +4. Investigate root cause +5. Implement fix with tests +6. Run Copilot code-review +7. Create PR and monitor CI +8. Mark ready when CI passes +9. **Capture learnings** → update agent plan with reusable patterns + +--- + +## 0. Environment Setup (MCP Servers) + +**Before using this plan, ensure the following MCP servers are configured.** + +### 0.1 Required MCP Servers & API Access + +| Tool | Purpose | Required | Setup | +|------|---------|----------|-------| +| **GitHub MCP Server** | Issues, PRs, code search, actions | ✅ Yes | Section 0.2 | +| **Azure DevOps PAT** | CI builds, logs, pipeline status | ✅ Yes | Section 0.3.1 | +| **Azure DevOps MCP** | Alternative to PAT (retry jobs) | Optional | Section 0.3.2 | +| **Cosmos LiveSite MCP** | LiveSite diagnostics, account info | Optional | Section 0.1.1 | +| **Bluebird Engineering Copilot** | Code graph, semantic search | Optional | Section 0.4 | + +### 0.1.1 Cosmos LiveSite MCP Server + +**Purpose:** Access Cosmos DB LiveSite diagnostics for investigating customer issues. + +**Prerequisites:** +1. Clone the CosmosDBLivesite repo +2. Set up Python virtual environment +3. Azure CLI login (`az login`) + +**Setup Instructions:** +```powershell +# 1. Clone the repo (if not already) +cd e:\src +git clone CosmosDBLivesite + +# 2. Create Python virtual environment +cd e:\src\CosmosDBLivesite\Livesite +python -m venv .venv +.\.venv\Scripts\activate +pip install -r requirements.txt + +# 3. Azure login (required for Kusto access) +az login +# Select appropriate subscription (e.g., CosmosDB-SDK-Dev) +``` + +**MCP Config** (add to `~/.copilot/mcp-config.json`): +```json +{ + "cosmos-livesite": { + "type": "stdio", + "command": "e:\\src\\CosmosDBLivesite\\Livesite\\.venv\\Scripts\\python.exe", + "args": [ + "-m", + "src.mcp_server.server" + ], + "tools": ["*"], + "env": { + "MCP_ENV": "dev", + "LIVESITE_TENANT": "cosmosdb", + "PYTHONPATH": "e:\\src\\CosmosDBLivesite\\Livesite" + } + } +} +``` + +```yaml +cosmos_livesite_mcp: + capabilities: + - "Account diagnostics" + - "NSP (Network Security Perimeter) status" + - "Regional endpoint information" + - "Service-side configuration" + - "Run Kusto queries against Support database" + + use_cases: + - "Investigate connectivity issues" + - "Check if account has NSP enabled" + - "Verify regional endpoint configuration" + - "Customer-reported service issues" + + when_to_use: + - "Issue involves network/connectivity problems" + - "Need to verify account configuration" + - "Debugging 'eastusstg' or similar endpoint issues" + + requires: + - "az login (Azure CLI authentication)" + - "Python 3.x with virtual environment" +``` + +### 0.2 GitHub Access (SAML-Protected Repos) + +**⚠️ IMPORTANT: Azure org repos require SAML SSO - GitHub MCP Server is BLOCKED.** + +```yaml +github_access: + saml_protected_orgs: + - "Azure" + - "microsoft" + note: "MCP server tokens don't have SAML authorization" + + what_works: + gh_cli: + status: "✅ WORKS" + reason: "Browser OAuth completes SAML SSO" + auth: "gh auth login --web" + use_for: + - "Create/list/view issues" + - "Create/list/view PRs" + - "Check PR status" + - "All GitHub operations" + examples: + - "gh issue list --repo Azure/azure-cosmos-dotnet-v3" + - "gh pr create --draft --title '...'" + - "gh pr checks 5583" + + web_fetch: + status: "✅ WORKS (fallback)" + reason: "Scrapes public web pages" + use_for: "Reading issue/PR content when gh fails" + example: "web_fetch https://github.com/Azure/azure-cosmos-dotnet-v3/issues/5547" + + what_does_not_work: + github_mcp_server: + status: "❌ BLOCKED by SAML" + error: "Resource protected by organization SAML enforcement" + reason: "MCP OAuth token not SAML-authorized" + tools_affected: + - "github-mcp-server-list_issues" + - "github-mcp-server-issue_read" + - "github-mcp-server-pull_request_read" + - "All github-mcp-server-* tools for Azure org" + + recommended_approach: + primary: "Use gh CLI for all GitHub operations" + fallback: "Use web_fetch to scrape issue content" + avoid: "Don't rely on GitHub MCP Server for Azure org repos" +``` + +**Azure CLI Installation & Setup:** + +```powershell +# 1. Install Azure CLI (Windows) +winget install Microsoft.AzureCLI --accept-source-agreements --accept-package-agreements + +# Alternative installers: +# - macOS: brew install azure-cli +# - Linux: curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash + +# 2. Authenticate (opens browser) +az login + +# 3. Select subscription when prompted +# For Cosmos DB SDK work, select: CosmosDB-SDK-Dev + +# 4. Verify authentication +az account show --query "{name:name, id:id}" -o table + +# 5. Set specific subscription if needed +az account set --subscription "CosmosDB-SDK-Dev" +``` + +```yaml +azure_cli_usage: + kusto_queries: + description: "Run Kusto queries against Support database" + example: | + az kusto query --cluster "https://cdbsupport.kusto.windows.net" \ + --database "Support" \ + --query "MgmtGlobalDatabaseAccountTrace | where GlobalDatabaseAccount contains 'account-name' | take 10" + + cosmos_account_info: + description: "Get Cosmos DB account information" + example: | + az cosmosdb show --name --resource-group + + recommended_subscriptions: + - "CosmosDB-SDK-Dev" # For SDK development + - "Cosmos DB Backend Testing" # For backend testing +``` + +**gh CLI Installation & Setup:** + +```powershell +# 1. Install gh CLI (Windows) +winget install --id GitHub.cli + +# Alternative installers: +# - macOS: brew install gh +# - Linux: https://github.com/cli/cli/blob/trunk/docs/install_linux.md + +# 2. Authenticate (opens browser for SAML SSO) +gh auth login --web + +# 3. Select options when prompted: +# - GitHub.com (not Enterprise) +# - HTTPS +# - Authenticate with browser + +# 4. Complete SAML SSO in browser +# - Login to GitHub +# - Authorize for Azure org (SAML) + +# 5. Verify authentication +gh auth status +# Expected: ✓ Logged in to github.com account {username} + +# 6. Test access to Azure org (with auto-fix) +gh issue list --repo Azure/azure-cosmos-dotnet-v3 --limit 1 +# If SAML error occurs, run steps 7-8 + +# 7. Force logout (if SAML error) +gh auth logout + +# 8. Re-login with SAML authorization +gh auth login --web +# → Complete browser auth AND authorize Azure org + +# 9. Verify fixed +gh issue list --repo Azure/azure-cosmos-dotnet-v3 --limit 1 +``` + +**Auto-Fix Script (Copy-Paste):** +```powershell +# Test and auto-fix SAML issues +$result = gh issue list --repo Azure/azure-cosmos-dotnet-v3 --limit 1 2>&1 +if ($result -match "SAML") { + Write-Host "SAML error detected. Re-authenticating..." + gh auth logout + gh auth login --web + Write-Host "Please authorize Azure org in browser, then press Enter..." + Read-Host + gh issue list --repo Azure/azure-cosmos-dotnet-v3 --limit 1 +} else { + Write-Host "✅ gh CLI working: $result" +} +``` + +**Required Scopes:** +```yaml +gh_token_scopes: + required: + - "repo" # Full repository access + - "read:org" # Read org membership (for SAML) + optional: + - "workflow" # GitHub Actions + - "gist" # Gists +``` + +**Troubleshooting SAML Errors:** + +```yaml +saml_troubleshooting: + error: "Resource protected by organization SAML enforcement" + cause: "Token not authorized for Azure org via SAML SSO" + + fix_steps: + step_1: + description: "Logout and re-authenticate with SAML" + commands: | + gh auth logout + gh auth login --web + + step_2: + description: "During browser auth, authorize for Azure org" + note: "After GitHub login, you'll see 'Authorize for: Azure' - click it" + + step_3: + description: "If no SAML prompt appears, manually authorize" + url: "https://github.com/settings/tokens" + steps: + - "Go to https://github.com/settings/tokens" + - "Find the 'gh' token (or GitHub CLI)" + - "Click 'Configure SSO' dropdown" + - "Click 'Authorize' next to 'Azure' org" + - "Complete SAML login" + + step_4: + description: "Verify authorization" + command: "gh issue list --repo Azure/azure-cosmos-dotnet-v3 --limit 1" + + alternative_manual_sso: + url: "https://github.com/orgs/Azure/sso" + steps: + - "Visit https://github.com/orgs/Azure/sso" + - "Complete SAML authentication" + - "Return and retry gh command" +``` + +**Quick Fix (Copy-Paste):** +```powershell +# Re-authenticate with SAML +gh auth logout +gh auth login --web +# → Complete browser auth AND click "Authorize" for Azure org + +# OR manually authorize existing token: +# 1. Go to: https://github.com/settings/tokens +# 2. Find GitHub CLI token → Configure SSO → Authorize Azure +``` + +**Quick Reference:** +```powershell +# ✅ This works (gh CLI) +gh issue view 5547 --repo Azure/azure-cosmos-dotnet-v3 +gh pr create --draft --title "Fix: ..." +gh pr checks 5583 + +# ❌ This fails (MCP Server) +# github-mcp-server-list_issues → SAML error +``` + +### 0.3 Azure DevOps Access (PAT or MCP) + +**Required for CI build logs, retry failed jobs, pipeline management.** + +#### 0.3.1 Option 1: Personal Access Token (PAT) - Recommended + +**Simplest setup - works immediately without additional tools.** + +```yaml +azure_devops_pat: + purpose: "Direct REST API access to Azure DevOps" + advantage: "No MCP server needed, works with PowerShell/curl" + + setup: + step_1: "Create PAT at https://dev.azure.com/{org}/_usersSettings/tokens" + step_2: "Select scope: Build (Read) - minimum required" + step_3: "Store PAT securely (environment variable, never in code)" + + environment_variable: + name: "ADO_PAT" + set_windows: '$env:ADO_PAT = "your-pat-here"' + set_unix: 'export ADO_PAT="your-pat-here"' + + organization: "cosmos-db-sdk-public" + project: "cosmos-db-sdk-public" + api_base: "https://dev.azure.com/cosmos-db-sdk-public/cosmos-db-sdk-public/_apis" +``` + +**Verify PAT Setup:** + +```powershell +# Test PAT is working +$headers = @{ Authorization = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$env:ADO_PAT")) } +Invoke-RestMethod -Uri "https://dev.azure.com/cosmos-db-sdk-public/cosmos-db-sdk-public/_apis/build/builds?api-version=7.0&`$top=1" -Headers $headers + +# Expected: JSON response with build info (not HTML or 401 error) +``` + +**Common API Calls:** + +```powershell +$headers = @{ Authorization = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$env:ADO_PAT")) } +$org = "cosmos-db-sdk-public" +$project = "cosmos-db-sdk-public" + +# Get build status +$buildId = 59172 # From PR checks URL +$build = Invoke-RestMethod -Uri "https://dev.azure.com/$org/$project/_apis/build/builds/$buildId?api-version=7.0" -Headers $headers +$build.status # "completed", "inProgress", etc. +$build.result # "succeeded", "failed", etc. + +# Get build timeline (all stages/jobs with status) +$timeline = Invoke-RestMethod -Uri "https://dev.azure.com/$org/$project/_apis/build/builds/$buildId/timeline?api-version=7.0" -Headers $headers + +# Find failed tasks +$failed = $timeline.records | Where-Object { $_.result -eq "failed" } +$failed | Select-Object name, result, @{N='logId';E={$_.log.id}} + +# Get log content for failed task +$logId = $failed[0].log.id +$log = Invoke-RestMethod -Uri "https://dev.azure.com/$org/$project/_apis/build/builds/$buildId/logs/$logId?api-version=7.0" -Headers $headers +$log | Select-String -Pattern "error|fail|assert" -Context 2,2 +``` + +#### 0.3.2 Option 2: Azure DevOps MCP Server + +**Alternative if MCP infrastructure is preferred.** + +```yaml +azure_devops_mcp_server: + package: "@azure-devops/mcp" + publisher: "Microsoft" + repository: "https://github.com/microsoft/azure-devops-mcp" + docs: "https://learn.microsoft.com/en-us/azure/devops/mcp-server/mcp-server-overview" + + capabilities: + pipelines: + - mcp_ado_pipelines_get_builds # List builds + - mcp_ado_pipelines_get_build_status # Get build status + - mcp_ado_pipelines_get_build_log # Get build logs + - mcp_ado_pipelines_run_pipeline # Start new pipeline run + - mcp_ado_pipelines_update_build_stage # ⭐ RETRY FAILED JOBS + work_items: + - mcp_ado_wit_get_work_item + - mcp_ado_wit_create_work_item + - mcp_ado_wit_my_work_items + repositories: + - mcp_ado_repo_list_pull_requests_by_repo_or_project + - mcp_ado_repo_get_pull_request_by_id +``` + +**Installation:** + +```bash +# Option 1: VS Code One-Click Install +# Visit: https://insiders.vscode.dev/redirect/mcp/install?name=ado&config=... + +# Option 2: Manual Setup +# Create .vscode/mcp.json in your project: +``` + +```json +{ + "inputs": [ + { + "id": "ado_org", + "type": "promptString", + "description": "Azure DevOps organization name (e.g. 'cosmos-db-sdk-public')" + } + ], + "servers": { + "ado": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@azure-devops/mcp", "${input:ado_org}"] + } + } +} +``` + +**Authentication:** +- First use will open browser for Microsoft account login +- Requires Azure DevOps organization access + +**Key Tool: Retry Failed Jobs** +```yaml +mcp_ado_pipelines_update_build_stage: + purpose: "Retry failed stages without re-running entire build" + parameters: + project: "cosmos-db-sdk-public" + buildId: 59156 + stageRefName: "Preview_Tests_Release" # Stage reference name + state: "retry" # Options: retry, cancel + + advantage: "Faster than requeuing entire build" +``` + +### 0.4 Bluebird Engineering Copilot (Optional) + +**For advanced code graph and semantic search.** + +```yaml +bluebird_engineering_copilot: + capabilities: + - do_vector_search: "Semantic code search" + - do_fulltext_search: "Keyword code search" + - get_source_code: "Retrieve source files" + - get_hierarchical_summary: "Code overview" + - get_callers, get_callees: "Call graph analysis" + + usage: + step_1: "Call engineering_copilot for instructions" + step_2: "Call dynamic_tool_invoker with specific tool" + + note: "Useful for deep code analysis, may timeout on large queries" +``` + +### 0.5 Quick Setup Script (Windows PowerShell) + +```powershell +# 1. Install GitHub CLI +winget install --id GitHub.cli + +# 2. Authenticate GitHub CLI +gh auth login --web + +# 3. Install Node.js (for Azure DevOps MCP) +winget install --id OpenJS.NodeJS + +# 4. Test Azure DevOps MCP (first run opens browser for auth) +npx @azure-devops/mcp cosmos-db-sdk-public --help +``` + +### 0.6 Verification Checklist + +```markdown +## Environment Setup Verification + +### GitHub MCP Server +- [ ] `gh auth status` shows logged in +- [ ] Can fetch issues: `gh issue list --repo Azure/azure-cosmos-dotnet-v3` + +### Azure DevOps MCP Server +- [ ] Node.js 18+ installed: `node --version` +- [ ] MCP server responds: `npx @azure-devops/mcp cosmos-db-sdk-public --help` +- [ ] Browser auth completed (first MCP tool call opens login) + +### Local Tools +- [ ] .NET SDK installed: `dotnet --version` +- [ ] Git configured: `git config user.name` +- [ ] Repository cloned: `E:\src\v33` or similar + +### Cosmos DB Emulator +- [ ] Emulator installed: Check "Azure Cosmos DB Emulator" in Start menu +- [ ] Emulator running: `https://localhost:8081/_explorer/index.html` accessible +``` + +### 0.7 New Machine Quick Start + +**To test these instructions on a new machine with a new issue:** + +```yaml +new_machine_setup: + time_estimate: "15-20 minutes" + + step_1_prerequisites: + commands: | + # Install required tools (Windows) + winget install Microsoft.DotNet.SDK.8 + winget install Git.Git + winget install GitHub.cli + winget install OpenJS.NodeJS + winget install Microsoft.Azure.CosmosEmulator + + step_2_clone_repo: + commands: | + # Clone repository + git clone https://github.com/Azure/azure-cosmos-dotnet-v3.git + cd azure-cosmos-dotnet-v3 + + # Build to verify setup + dotnet build Microsoft.Azure.Cosmos.sln -c Release + + step_3_authenticate: + commands: | + # GitHub CLI + gh auth login --web + + # Verify + gh auth status + + step_4_start_emulator: + commands: | + # Start Cosmos DB Emulator + Start-Process "C:\Program Files\Azure Cosmos DB Emulator\CosmosDB.Emulator.exe" + + # Wait 30 seconds, then verify + Start-Sleep -Seconds 30 + Invoke-WebRequest -Uri "https://localhost:8081/" -UseBasicParsing + + step_5_test_with_issue: + description: "Give Copilot an issue to work on" + example_prompt: | + "Investigate and fix issue #XXXX following the plan in + .github/copilot-agent-plan.md" +``` + +**Test Workflow with a Real Issue:** + +```yaml +testing_the_plan: + step_1_select_issue: + options: + - "Pick an open bug from: gh issue list --label bug --state open" + - "Use a known test issue (if available)" + - "Create a test issue with a known problem" + + step_2_invoke_copilot: + prompt_template: | + Follow the Copilot Agent Issue Triage Plan in .github/copilot-agent-plan.md + + Investigate issue #{issue_number}: {issue_title} + + Steps: + 1. Fetch and analyze the issue + 2. Search codebase for related code + 3. Identify root cause + 4. Create reproduction test + 5. Implement fix + 6. Run local tests (unit + emulator) + 7. Create draft PR + 8. Monitor CI until GREEN + + step_3_verify_workflow: + checklist: + - "[ ] Issue fetched successfully (or via web_fetch fallback)" + - "[ ] Root cause identified" + - "[ ] Local tests pass" + - "[ ] PR created with correct format" + - "[ ] CI monitoring initiated" + + step_4_validate_results: + success_criteria: + - "PR created following naming convention" + - "PR title matches lint format" + - "Local emulator tests pass" + - "CI gates monitored" +``` + +**Minimal Test (5 minutes):** + +```powershell +# Quick verification that setup works +# Run from repository root + +# 1. Verify build +dotnet build Microsoft.Azure.Cosmos.sln -c Release + +# 2. Run unit tests (no emulator needed, no external dependencies) +dotnet test .\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests -c Release --no-restore + +# 3. Verify GitHub CLI +gh issue list --repo Azure/azure-cosmos-dotnet-v3 --limit 5 + +# 4. Verify git +git status + +# If all pass, environment is ready for Copilot agent workflow +Write-Host "✅ Environment ready!" +``` + +**Troubleshooting Common Issues:** + +```yaml +troubleshooting: + build_fails: + symptom: "dotnet build fails" + causes: + - "Missing .NET SDK": "winget install Microsoft.DotNet.SDK.8" + - "Wrong SDK version": "Check global.json for required version" + - "Missing workloads": "dotnet workload restore" + + gh_auth_fails: + symptom: "gh: not authenticated" + fix: "gh auth login --web" + + emulator_not_starting: + symptom: "Connection refused on localhost:8081" + causes: + - "Emulator not installed": "winget install Microsoft.Azure.CosmosEmulator" + - "Port conflict": "Check if another process uses 8081" + - "Needs admin": "Run emulator as administrator" + + github_api_403: + symptom: "SAML authentication required" + fix: "Use web_fetch to scrape issue page directly (see Section 3 Phase 0)" + + azure_devops_auth: + symptom: "MCP server auth fails" + fix: "First MCP call opens browser - complete Microsoft login" +``` + +--- + +## 1. Executive Summary + +This plan defines a comprehensive workflow for Copilot agents to handle GitHub issues for the Azure Cosmos DB .NET SDK repository. The agent will: +- Triage all issue types (bugs, features, questions, documentation) +- Reproduce issues against the Cosmos DB emulator (when applicable) +- Leverage Bluebird code graph tools for deep code analysis +- Create linked investigation issues with structured findings +- Auto-assign to human reviewers based on labels/area +- Propose workarounds and draft PRs when appropriate + +--- + +## 1.0 Core Principles: No Shortcuts, Evidence Required + +> ⚠️ **CRITICAL: These principles are non-negotiable and apply to ALL steps in this plan.** + +### 1.0.1 No Shortcuts Policy + +```yaml +no_shortcuts_policy: + principle: "Every step in the workflow MUST be executed explicitly" + + rules: + - "Do NOT skip steps even if outcome seems obvious" + - "Do NOT assume a step succeeded without verification" + - "Do NOT combine steps to save time at the cost of rigor" + - "Do NOT mark checklist items complete without actually doing them" + - "Do NOT proceed to next phase until current phase is verified" + - "Do NOT wait or ask permission when next action is clear" + - "Do NOT ask 'want me to do X?' - just execute if conditions are met" + + immediate_action_triggers: + - "CI passes on draft PR → immediately mark ready for review" + - "CI fails → immediately investigate and fix" + - "PR lint fails → immediately fix title/description" + - "Tests pass locally → immediately push and create PR" + - "Issue comment needed → immediately post it" + - "PR review comment received → immediately address or respond" + - "PR changes requested → immediately fix and push" + - "PR approved → monitor for merge or follow up" + + examples_of_violations: + - "Assuming tests pass without running them" + - "Skipping local build because 'it should work'" + - "Not posting issue comment because 'PR is self-explanatory'" + - "Marking 'root cause identified' without code path trace" + - "Skipping reproduction because 'issue description is clear'" + - "Asking 'should I mark PR ready?' when CI already passed" + - "Waiting for user confirmation on routine next steps" + + enforcement: + - "Each step must produce observable output" + - "Session plan must track completion with evidence" + - "Reviewers can request proof of any claimed step" +``` + +### 1.0.2 Evidence-Based Verification + +```yaml +evidence_required_policy: + principle: "Every outcome/output requires PROOF, not assumptions" + + what_counts_as_proof: + build_succeeded: + required: "Actual build output showing 'Build succeeded' or exit code 0" + not_acceptable: "I ran the build" (without output) + + tests_passed: + required: "Test output showing pass count: 'Passed: X, Failed: 0'" + not_acceptable: "Tests should pass" or "I ran the tests" + + root_cause_identified: + required: "Code path with file:line references showing exactly where bug occurs" + not_acceptable: "The issue is in the LINQ translator" + + fix_works: + required: "Before/after output demonstrating the fix" + not_acceptable: "The fix addresses the issue" + + ci_passed: + required: "CI status showing 'COMPLETED: X/X' with all green" + not_acceptable: "CI is running" or "CI should pass" + + issue_updated: + required: "GitHub comment URL as confirmation" + not_acceptable: "I posted a comment" + + verification_format: + pattern: | + **Step:** {step_name} + **Evidence:** {actual_output_or_url} + **Status:** ✅ Verified + + session_plan_tracking: + format: | + - [x] Build succeeded (exit code 0, 0 errors) + - [x] Tests passed (13/13 LINQ, 12/12 BuiltinFunction) + - [x] PR created (https://github.com/.../pull/5585) + - [x] Comment posted (https://github.com/.../issues/5518#comment-123) +``` + +### 1.0.3 Mandatory Checkpoints + +```yaml +mandatory_checkpoints: + before_creating_branch: + - "Search for existing PRs: `gh pr list --search 'ISSUE_NUMBER'`" + - "Search for existing branches: `git branch -a | grep ISSUE_NUMBER`" + - "If PR exists, use existing branch instead of creating new one" + - "Document search results as evidence" + + before_creating_pr: + - "Local build MUST succeed with captured output" + - "Local tests MUST pass with captured output" + - "Root cause MUST be documented with code path" + - "Fix MUST be verified with before/after evidence" + - "PR MUST be created as DRAFT: `gh pr create --draft`" + + before_marking_pr_ready: + - "ALL CI gates MUST show COMPLETED status" + - "PR description MUST follow full template" + - "Issue comment MUST be posted with investigation summary" + - "Convert from draft: `gh pr ready `" + + before_claiming_issue_resolved: + - "PR MUST be merged (not just created)" + - "Or customer MUST confirm workaround works" + - "Or issue MUST be closed with documented reason" + + checkpoint_failure_response: + action: "STOP and address the failure" + do_not: "Proceed hoping it will resolve itself" + document: "Record what failed and remediation steps" +``` + +### 1.0.4 Anti-Patterns to Avoid + +```yaml +anti_patterns: + assumption_based_claims: + bad: "The fix should work because the logic is correct" + good: "The fix works - test output shows 13/13 passed" + + implicit_verification: + bad: "I updated the PR" + good: "PR updated: https://github.com/.../pull/5585" + + skipped_local_validation: + bad: "Pushing to CI to validate" + good: "Local build: ✅, Local tests: 13/13 ✅, Emulator tests: 45/45 ✅, now pushing to CI" + + incomplete_documentation: + bad: "Fixed the LINQ issue" + good: "Fixed: BuiltinFunctionVisitor.cs:108 - added IsMemoryExtensionsMethod() check" + + optimistic_status: + bad: "CI running, should pass" + good: "CI status: 7/33 completed, monitoring for failures" + + partial_test_validation: + bad: "Unit tests pass, emulator tests will run in CI" + good: "Unit tests: 13/13 ✅, Emulator tests: 45/45 ✅, all local validation complete" + + duplicate_pr_creation: + bad: "Creating new branch and PR for issue #1234" + good: "Checked for existing PRs: `gh pr list --search '1234'` - none found, creating new PR" + rule: "ALWAYS search for existing PRs/branches before creating new ones" + + branch_without_pr_check: + bad: "git checkout -b users/me/fix-issue-1234" + good: "First: `gh pr list --search '1234'` and `git branch -a | grep 1234` - then create if none exist" + + pr_created_as_ready: + bad: "gh pr create --title 'Fix issue'" + good: "gh pr create --draft --title 'Fix issue'" + rule: "ALWAYS create PRs as draft, convert to ready only after CI passes" + + fix_at_wrong_layer: + bad: "Add null check at caller when the source method returns null incorrectly" + good: "Fix at source of truth - if GetStoreProxy() can return null when disposed, throw ObjectDisposedException there" + rule: "ALWAYS fix bugs at the source where the invalid state originates, not at callers" + principle: | + When a method returns an invalid value (null, error state), fix the method itself + rather than adding defensive checks in all callers. This: + - Provides clear error messages at the source + - Prevents the same bug pattern in other callers + - Keeps error handling centralized +``` + +### 1.0.5 Mandatory Local Test Validation Before Push + +> ⛔ **CRITICAL: Every commit/change MUST have ALL tests validated locally BEFORE push.** + +```yaml +local_test_validation_policy: + principle: "No push without complete local test validation" + + required_before_every_push: + step_1_build: + command: "dotnet build Microsoft.Azure.Cosmos.sln -c Release" + required: true + evidence: "Build succeeded. 0 Warning(s) 0 Error(s)" + + step_2_unit_tests: + command: "dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests -c Release" + required: true + evidence: "Passed! - Failed: 0, Passed: X" + time: "~2-5 minutes" + + step_3_emulator_tests: + command: "dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests -c Release" + required: true + evidence: "Passed! - Failed: 0, Passed: X" + time: "~15-30 minutes" + prerequisite: "Cosmos DB Emulator running with admin rights" + + emulator_setup: + start_command: "Start-Process 'C:\\Program Files\\Azure Cosmos DB Emulator\\CosmosDB.Emulator.exe' -Verb RunAs" + verify_running: "Get-Process -Name 'CosmosDB.Emulator' -ErrorAction SilentlyContinue" + wait_for_ready: "Invoke-WebRequest -Uri 'https://localhost:8081/_explorer/emulator.pem' -UseBasicParsing" + note: "Emulator requires admin elevation - run PowerShell as Administrator" + + no_exceptions: + - "CI is NOT a substitute for local validation" + - "Emulator tests are NOT optional" + - "Time constraints do NOT justify skipping tests" + - "Previous passing CI does NOT validate new changes" + + blocking_push_scenarios: + - "Emulator not installed → Install emulator first" + - "Emulator not running → Start emulator with admin rights" + - "Emulator tests fail → Fix failures before push" + - "Unit tests fail → Fix failures before push" + - "Build fails → Fix build before push" + + evidence_format: + template: | + ## Local Validation (Before Push) + + **Build:** + ``` + Build succeeded. + 0 Warning(s) + 0 Error(s) + Time Elapsed 00:00:XX.XX + ``` + + **Unit Tests:** + ``` + Passed! - Failed: 0, Passed: XX, Skipped: X, Total: XX + ``` + + **Emulator Tests:** + ``` + Passed! - Failed: 0, Passed: XX, Skipped: X, Total: XX + ``` + + workflow: + 1: "Make code changes" + 2: "Run build locally → capture output" + 3: "Run unit tests locally → capture output" + 4: "Start emulator (admin required)" + 5: "Run emulator tests locally → capture output" + 6: "All pass? → git commit and push" + 7: "Any fail? → Fix and repeat from step 2" +``` + +### 1.0.6 Emulator Test Commands (From Pipeline YAML) + +> ⚠️ **CRITICAL: Use EXACTLY the same arguments as the CI pipeline (templates/build-test.yml)** + +```yaml +pipeline_test_arguments: + source: "templates/build-test.yml" + + # Pipeline 1: Client Telemetry, Query, ChangeFeed, ReadFeed, Batch + emulator_pipeline_1: + name: "Client Telemetry, Query, ChangeFeed, ReadFeed, Batch" + filter: '--filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed) & TestCategory!=Ignore"' + command: | + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/*.csproj --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed) & TestCategory!=Ignore" --verbosity normal --configuration Release /p:OS=Windows + env: + AZURE_COSMOS_NON_STREAMING_ORDER_BY_FLAG_DISABLED: "true" + + # Pipeline 2: Others (everything not in Pipeline 1) + emulator_pipeline_2: + name: "Others" + filter: '--filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & TestCategory!=Ignore"' + command: | + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/*.csproj --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & TestCategory!=Ignore" --verbosity normal --configuration Release /p:OS=Windows + env: + AZURE_COSMOS_NON_STREAMING_ORDER_BY_FLAG_DISABLED: "true" + + # Pipeline 3: MultiRegion (requires connection string) + emulator_pipeline_3: + name: "MultiRegion" + filter: '--filter "TestCategory=MultiRegion"' + command: | + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/*.csproj --filter "TestCategory=MultiRegion" --verbosity normal --configuration Release /p:OS=Windows + requires: "COSMOSDB_MULTI_REGION connection string" + local_skip: true # Skip locally unless you have multi-region setup + + # Pipeline 4: MultiMaster (requires connection string) + emulator_pipeline_4: + name: "MultiMaster" + filter: '--filter "TestCategory=MultiMaster"' + command: | + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/*.csproj --filter "TestCategory=MultiMaster" --verbosity normal --configuration Release /p:OS=Windows + requires: "COSMOSDB_MULTI_REGION connection string (multi-master)" + local_skip: true # Skip locally unless you have multi-master setup + +local_test_commands: + description: "Run these commands EXACTLY as shown for local validation" + + unit_tests: + command: | + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/*.csproj --configuration Release /p:OS=Windows --verbosity normal + + emulator_tests_pipeline_1: + name: "Emulator Tests - Query, ChangeFeed, ReadFeed, Batch" + command: | + $env:AZURE_COSMOS_NON_STREAMING_ORDER_BY_FLAG_DISABLED = "true" + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/*.csproj --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed) & TestCategory!=Ignore" --verbosity normal --configuration Release /p:OS=Windows + time: "~20-30 minutes" + + emulator_tests_pipeline_2: + name: "Emulator Tests - Others" + command: | + $env:AZURE_COSMOS_NON_STREAMING_ORDER_BY_FLAG_DISABLED = "true" + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/*.csproj --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & TestCategory!=Ignore" --verbosity normal --configuration Release /p:OS=Windows + time: "~20-30 minutes" + + full_local_validation_script: + description: "Complete local validation script matching CI" + script: | + # Set environment variable (same as CI) + $env:AZURE_COSMOS_NON_STREAMING_ORDER_BY_FLAG_DISABLED = "true" + + # 1. Unit Tests + Write-Host "=== Running Unit Tests ===" -ForegroundColor Cyan + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/*.csproj --configuration Release /p:OS=Windows --verbosity normal + if ($LASTEXITCODE -ne 0) { Write-Host "❌ Unit tests failed" -ForegroundColor Red; exit 1 } + + # 2. Emulator Tests - Pipeline 1 (Query, ChangeFeed, ReadFeed, Batch) + Write-Host "=== Running Emulator Tests (Pipeline 1) ===" -ForegroundColor Cyan + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/*.csproj --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed) & TestCategory!=Ignore" --verbosity normal --configuration Release /p:OS=Windows + if ($LASTEXITCODE -ne 0) { Write-Host "❌ Emulator tests (Pipeline 1) failed" -ForegroundColor Red; exit 1 } + + # 3. Emulator Tests - Pipeline 2 (Others) + Write-Host "=== Running Emulator Tests (Pipeline 2) ===" -ForegroundColor Cyan + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/*.csproj --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & TestCategory!=Ignore" --verbosity normal --configuration Release /p:OS=Windows + if ($LASTEXITCODE -ne 0) { Write-Host "❌ Emulator tests (Pipeline 2) failed" -ForegroundColor Red; exit 1 } + + Write-Host "✅ All local tests passed!" -ForegroundColor Green +``` + +### 1.0.7 Emulator Environment Setup + +```yaml +emulator_environment: + installation: + download: "https://aka.ms/cosmosdb-emulator" + path: "C:\\Program Files\\Azure Cosmos DB Emulator\\CosmosDB.Emulator.exe" + + starting_emulator: + powershell_admin: | + # Must run PowerShell as Administrator + Start-Process "C:\Program Files\Azure Cosmos DB Emulator\CosmosDB.Emulator.exe" -Verb RunAs + + # Wait for emulator to be ready + $maxWait = 120 # seconds + $waited = 0 + while ($waited -lt $maxWait) { + try { + $response = Invoke-WebRequest -Uri "https://localhost:8081/_explorer/emulator.pem" -UseBasicParsing -ErrorAction Stop + Write-Host "✅ Emulator is ready" + break + } catch { + Write-Host "Waiting for emulator... ($waited s)" + Start-Sleep -Seconds 5 + $waited += 5 + } + } + + troubleshooting: + elevation_error: + symptom: "The requested operation requires elevation" + solution: "Run PowerShell as Administrator" + + port_in_use: + symptom: "Port 8081 already in use" + solution: "Stop existing emulator: Stop-Process -Name CosmosDB.Emulator" + + ssl_error: + symptom: "SSL certificate error" + solution: "Import emulator certificate or use -SkipCertificateCheck" +``` + +--- + +## 1.1 Model Configuration + +**Primary Model: Claude Opus 4.5** (`claude-opus-4.5`) + +Use Claude Opus for all investigation and analysis tasks due to its superior reasoning capabilities for complex debugging scenarios. + +```yaml +model_configuration: + primary_model: "claude-opus-4.5" + + task_model_mapping: + # Deep analysis tasks - use Opus + issue_triage: "claude-opus-4.5" + root_cause_analysis: "claude-opus-4.5" + code_investigation: "claude-opus-4.5" + pr_review: "claude-opus-4.5" + documentation_writing: "claude-opus-4.5" + + # Quick tasks - can use faster models if needed + simple_queries: "claude-opus-4.5" # Keep Opus for consistency + file_search: "claude-opus-4.5" + +agent_invocation: + explore_agent: + model: "claude-opus-4.5" + use_for: "Codebase exploration and understanding" + + task_agent: + model: "claude-opus-4.5" + use_for: "Build, test, reproduction execution" + + general_purpose_agent: + model: "claude-opus-4.5" + use_for: "Complex multi-step investigations" + + code_review_agent: + model: "claude-opus-4.5" + use_for: "PR and code change review" +``` + +**Why Claude Opus:** +- Superior reasoning for complex debugging scenarios +- Better at understanding nuanced code behavior +- More accurate root cause analysis +- Higher quality documentation generation +- Better at synthesizing information from multiple sources + +--- + +## 2. Issue Intake & Classification + +### 2.1 Issue Triggers +```yaml +triggers: + - new_issue_created + - issue_labeled: ["needs-triage", "bug", "question"] + - issue_commented: ["@copilot investigate", "@copilot help"] +``` + +### 2.2 Classification Matrix + +| Issue Type | Reproduction Required | Code Analysis Depth | Output | +|------------|----------------------|---------------------|--------| +| **Bug - Crash/Exception** | Yes - Full repro | Deep (call graph, stack trace mapping) | Investigation issue + Draft PR | +| **Bug - Performance** | Yes - Benchmark | Medium (hot path analysis) | Investigation issue + Perf report | +| **Bug - Data Corruption** | Yes - Careful repro | Deep (serialization path) | Investigation issue + CRITICAL label | +| **Question - How-to** | No | Light (find examples) | Comment with answer | +| **Question - Behavior** | Maybe | Medium (find relevant code) | Investigation issue if complex | +| **Enhancement** | No | Medium (impact analysis) | Feasibility comment | +| **Documentation** | No | Light | Draft PR | + +### 2.3 Standard Labels to Apply +```yaml +labels: + type: + - bug + - enhancement + - question + - documentation + area: + - Batch + - Query + - ChangeFeed + - DirectMode + - GatewayMode + - Serialization + - Encryption + - Diagnostics + - Retry + - PartitionKey + priority: + - P0-critical + - P1-high + - P2-medium + - P3-low + status: + - needs-triage + - investigating + - needs-repro + - has-workaround + - ready-for-pr +``` + +--- + +## 3. Triage Workflow + +### Phase 0: Issue Retrieval + +**Handle GitHub API limitations (SAML enforcement for Azure org):** + +```yaml +issue_retrieval: + primary_method: + tool: "github-mcp-server-issue_read" + params: + method: "get" + owner: "Azure" + repo: "azure-cosmos-dotnet-v3" + issue_number: "{number}" + + fallback_on_saml_error: + trigger: "403 error with 'Resource protected by organization SAML enforcement'" + tool: "web_fetch" + url: "https://github.com/Azure/azure-cosmos-dotnet-v3/issues/{number}" + note: "Scrapes issue page directly, bypasses API authentication" + + error_handling: + saml_error: + message: "GitHub API blocked by SAML - using web fallback" + action: "Switch to web_fetch automatically" + not_found: + message: "Issue #{number} not found" + action: "Ask user to verify issue number" +``` + +### Phase 1: Initial Assessment (< 5 minutes) + +```mermaid +flowchart TD + A[New Issue] --> B{Has repro steps?} + B -->|Yes| C{Has code sample?} + B -->|No| D[Request repro steps] + C -->|Yes| E[Extract key info] + C -->|No| F[Request minimal repro] + E --> G{Issue type?} + G -->|Bug| H[Start Bug Workflow] + G -->|Question| I[Start Question Workflow] + G -->|Enhancement| J[Start Enhancement Workflow] + G -->|Docs| K[Start Docs Workflow] +``` + +#### Information Extraction Checklist +```markdown +- [ ] SDK Version (from issue or code) +- [ ] .NET Version (runtime) +- [ ] Connection Mode (Direct/Gateway) +- [ ] Operation Type (CRUD, Query, Batch, ChangeFeed) +- [ ] Error Message / Exception Type +- [ ] Stack Trace (if available) +- [ ] Cosmos DB Account Type (Serverless/Provisioned) +- [ ] Partition Key configuration +- [ ] Custom serializer in use? +- [ ] Retry policy configuration +``` + +### Phase 1.5: Confirmation Gate ⚠️ + +**Before proceeding with investigation, present findings to user and ask for confirmation:** + +```markdown +## Investigation Scope Confirmation + +I've analyzed issue #{number} and gathered the following initial context: + +**Issue Summary:** +- Type: {bug/question/enhancement/docs} +- Area: {Batch/Query/ChangeFeed/etc} +- Reported SDK Version: {version} +- Priority Assessment: {P0-P3} + +**Proposed Investigation Plan:** +1. Historical search: GitHub issues, PRs, StackOverflow, changelog +2. Code analysis: {specific areas to investigate} +3. Reproduction: Test against {reported version}, {latest SDK}, {master branch} + +**Estimated Time:** {X minutes} + +**Proceed with investigation?** +- [ ] Yes, proceed as planned +- [ ] Yes, but modify scope: {specify} +- [ ] No, need more information first +- [ ] Skip reproduction (analysis only) +``` + +> ⚠️ **MANDATORY**: Always wait for user confirmation before starting resource-intensive investigation or reproduction steps. + +--- + +### Phase 2: Historical Analysis (< 10 minutes) + +#### 2a. Search for Related Issues (GitHub) +``` +Tools to use: +- github-mcp-server-search_issues: Find similar past issues +- github-mcp-server-list_pull_requests: Find related fixes +- git log --grep: Search commit history +``` + +**Search patterns:** +```bash +# Search by exception type +github search issues: "CosmosException" + "" + +# Search by operation +github search issues: "TransactionalBatch" + "timeout" + +# Search changelog +grep -i "" changelog.md +``` + +#### 2b. Search External Public References + +**StackOverflow Search:** +```yaml +tool: web_search +queries: + - "azure-cosmosdb {error message} site:stackoverflow.com" + - "Microsoft.Azure.Cosmos {operation} {issue keyword} site:stackoverflow.com" + - "{exception type} cosmos db .net sdk site:stackoverflow.com" +``` + +**Other Trusted Sources:** +```yaml +trusted_sources: + - stackoverflow.com (tag: azure-cosmosdb) + - docs.microsoft.com/azure/cosmos-db + - devblogs.microsoft.com/cosmosdb + - github.com/Azure/azure-cosmos-dotnet-v3/discussions + - techcommunity.microsoft.com (Cosmos DB tag) + +search_tool: web_search +query_template: "{issue keywords} {error/behavior} site:{source}" +``` + +**What to Extract from External Sources:** +- Known workarounds from community +- Similar issue patterns and resolutions +- Version-specific behavior changes +- Configuration recommendations +- Performance tuning tips + +#### 2c. Check Changelog for Related Fixes +```yaml +search_in: + - changelog.md + - PULL_REQUEST_TEMPLATE.md (for PR patterns) + - docs/releaseNotes/*.md +``` + +#### 2d. Search Commit History +```bash +git log --all --oneline --grep="" -- +git log --all --oneline -S "" # Search for code changes +``` + +### Phase 3: Code Analysis + +#### Phase 2.5: Expectations Validation ⚠️ + +**Before diving into code analysis, validate that the reported expectations are correct.** + +```yaml +expectations_validation: + purpose: "Verify the user's expected behavior is actually correct" + importance: "Users sometimes report 'bugs' that are actually expected behavior" + + validation_sources: + official_documentation: + - docs.microsoft.com/azure/cosmos-db + - Azure SDK for .NET documentation + - API reference documentation + - Cosmos DB REST API specifications + + code_documentation: + - XML doc comments in source code + - README files in relevant folders + - Inline code comments explaining behavior + - Test files (show expected behavior) + + sdk_samples: + - Microsoft.Azure.Cosmos.Samples/ + - Official Azure SDK samples repo + - Code snippets in documentation + + cosmos_db_specifications: + - SQL query language reference + - Consistency level behaviors + - Partitioning rules + - Request unit (RU) calculations +``` + +**Validation Checklist:** +```markdown +## Pre-Investigation Checks + +Before investigating further, verify: + +### 0. Is there already an open PR for this issue? +- [ ] Check linked PRs: `gh pr list --search "fixes #XXXX" --state open` +- [ ] Check issue comments for PR references +- [ ] If PR exists: Review PR instead of duplicating work +- [ ] If PR exists but stale: Consider taking over or commenting + +### 1. Is the expected behavior documented? +- [ ] Checked official Microsoft Docs for the feature +- [ ] Reviewed API reference documentation +- [ ] Found relevant SDK samples + +### 2. Does the code documentation support the expectation? +- [ ] Reviewed XML doc comments on relevant methods +- [ ] Checked for documented limitations or known behaviors +- [ ] Reviewed test files for expected behavior patterns + +### 3. Is this a documented limitation? +- [ ] Searched docs for "limitations" or "not supported" +- [ ] Checked Cosmos DB SQL reference for supported operations +- [ ] Reviewed changelog for intentional behavior changes + +### 4. Validation Result +- [ ] **CONFIRMED**: User expectation is correct (proceed with bug fix) +- [ ] **INCORRECT**: User expectation doesn't match documented behavior (educate user) +- [ ] **UNCLEAR**: Documentation is ambiguous or missing (may need docs improvement) +- [ ] **UNDOCUMENTED**: Feature behavior is not documented (investigate actual behavior) +``` + +**Validation Workflow:** +```yaml +validation_workflow: + step_1_check_docs: + tool: web_fetch + urls: + - "https://docs.microsoft.com/azure/cosmos-db/{feature}" + - "https://docs.microsoft.com/dotnet/api/microsoft.azure.cosmos.{class}" + extract: + - Expected behavior description + - Supported operations + - Known limitations + + step_2_check_code_docs: + tools: + - grep: "/// " in relevant files + - view: XML doc comments on methods + extract: + - Method documentation + - Parameter constraints + - Return value expectations + - Exception conditions + + step_3_check_samples: + tools: + - glob: "Microsoft.Azure.Cosmos.Samples/**/*.cs" + - grep: "{feature keyword}" in samples + extract: + - Recommended usage patterns + - Working code examples + + step_4_check_tests: + tools: + - glob: "**/tests/**/*{feature}*.cs" + - view: Test methods showing expected behavior + extract: + - Assertions showing expected outcomes + - Edge cases handled + - Known limitations tested +``` + +**Example Validation Outcomes:** + +| Scenario | User Expectation | Documentation Says | Action | +|----------|------------------|-------------------|--------| +| Dictionary LINQ | `.Any()` should work | Not documented for Dictionary | Investigate, likely needs fix | +| Null partition key | Should auto-generate | Docs say must provide value | Educate user | +| Cross-partition query | Should be fast | Docs warn about RU cost | Educate user | +| Retry on 429 | Should auto-retry | Docs confirm auto-retry | Investigate why not working | + +**Documentation Sources to Check:** + +```yaml +microsoft_docs: + cosmos_db_overview: "https://docs.microsoft.com/azure/cosmos-db/" + sql_query_reference: "https://docs.microsoft.com/azure/cosmos-db/sql/sql-query-getting-started" + linq_support: "https://docs.microsoft.com/azure/cosmos-db/sql/sql-query-linq-to-sql" + partitioning: "https://docs.microsoft.com/azure/cosmos-db/partitioning-overview" + consistency_levels: "https://docs.microsoft.com/azure/cosmos-db/consistency-levels" + +sdk_api_reference: + cosmos_client: "https://docs.microsoft.com/dotnet/api/microsoft.azure.cosmos.cosmosclient" + container: "https://docs.microsoft.com/dotnet/api/microsoft.azure.cosmos.container" + query: "https://docs.microsoft.com/dotnet/api/microsoft.azure.cosmos.feediterator" + +code_documentation: + xml_docs: "Search for /// comments in source files" + readme_files: "Check README.md in relevant directories" + inline_comments: "Look for // comments explaining behavior" +``` + +--- + +#### 3a. Combined Analysis Approach: Local + Bluebird + +**Use BOTH local tools and Bluebird for comprehensive analysis:** + +```yaml +analysis_strategy: + local_tools_for: + - Quick exact-match searches (grep) + - File discovery (glob) + - Reading specific known files (view) + - String literal searches + - Configuration file analysis + + bluebird_tools_for: + - Semantic/conceptual searches + - Call graph traversal + - Inheritance hierarchy + - Code summaries and understanding + - Complex relationship queries +``` + +**Local Tools Workflow:** +```bash +# Find files related to issue area +glob: "**/*{keyword}*.cs" + +# Search for error messages, constants +grep: pattern="{error code}" glob="*.cs" -n + +# Search for specific patterns +grep: pattern="throw.*{ExceptionType}" glob="*.cs" -C 3 + +# Find configuration/constants +grep: pattern="{config key}" path="src/" -n +``` + +**Bluebird Tools Workflow:** +```yaml +analysis_workflow: + 1_locate: + tool: do_vector_search + input: + similarity_search_text: "" + search_index: "Function" | "Class" | "General" + top_k: 10 + + 2_understand: + tool: get_hierarchical_summary + input: + node_name: "" + node_type: "Class" | "Function" + + 3_trace_calls: + tool: get_function_calling_functions + input: + function_name: "" + # Follow call chain to find root cause + + 4_get_code: + tool: get_source_code + input: + node_name: "" + node_type: "Function" +``` + +**Combined Analysis Example:** +```yaml +investigation_flow: + step_1: + description: "Quick local search for error/keyword" + tool: grep + purpose: "Fast initial scan" + + step_2: + description: "Semantic search for related functionality" + tool: bluebird.do_vector_search + purpose: "Find conceptually related code" + + step_3: + description: "Read specific files found" + tool: view + purpose: "Examine exact code" + + step_4: + description: "Trace call relationships" + tool: bluebird.get_function_calling_functions + purpose: "Understand code flow" + + step_5: + description: "Cross-reference with tests" + tool: grep + glob + purpose: "Find existing test coverage" +``` + +#### 3b. Analysis by Issue Type + +**For Exceptions/Crashes:** +```yaml +steps: + - Parse stack trace to identify top frame in SDK code + - Use get_function_calling_functions to trace call path + - Use get_function_called_functions to understand what failed + - Identify error handling paths with grep for "throw" patterns + - Check if retry logic should have caught this +``` + +**For Performance Issues:** +```yaml +steps: + - Identify hot path using operation type + - Check serialization path (CosmosSerializerCore) + - Check connection/retry configuration + - Look for blocking calls (sync over async) + - Analyze diagnostics output if provided +``` + +**For Data/Serialization Issues:** +```yaml +steps: + - Identify serializer in use + - Trace serialization path (ToStream/FromStream) + - Check partition key extraction logic + - Verify JSON path handling + - Check for custom serializer conflicts +``` + +--- + +## 4. Reproduction Workflow + +### 4.1 Multi-Version Reproduction Strategy + +**Test against THREE versions to determine fix status:** + +```yaml +reproduction_versions: + 1_reported_version: + description: "Version from issue report" + purpose: "Confirm issue exists as reported" + source: "NuGet package" + + 2_latest_stable: + description: "Latest released SDK version" + purpose: "Check if already fixed in release" + source: "NuGet package (latest)" + + 3_master_branch: + description: "Current master/main branch" + purpose: "Check if fix exists but not released" + source: "Local build from master" +``` + +**Version Matrix Test Results:** + +| Version | Result | Implication | +|---------|--------|-------------| +| Reported ✅ / Latest ✅ / Master ✅ | Issue persists | Needs fix | +| Reported ✅ / Latest ❌ / Master ❌ | Fixed in release | Recommend upgrade | +| Reported ✅ / Latest ✅ / Master ❌ | Fixed, not released | Mention upcoming fix | +| Reported ❌ / Latest ❌ / Master ❌ | Cannot reproduce | Request more info | + +### 4.2 Environment Setup + +```yaml +prerequisites: + - Cosmos DB Emulator (Windows) or Linux emulator + - .NET SDK matching issue reporter's version + - Connection modes: Direct + Gateway + +emulator_setup: + script: templates/emulator-setup.yml + connection_string: "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" + +version_setup: + reported_version: + command: | + # Create temp project with specific version + dotnet new console -o IssueRepro_{number}_v{reported} + cd IssueRepro_{number}_v{reported} + dotnet add package Microsoft.Azure.Cosmos --version {reported_version} + + latest_version: + command: | + dotnet new console -o IssueRepro_{number}_latest + cd IssueRepro_{number}_latest + dotnet add package Microsoft.Azure.Cosmos # Gets latest + + master_branch: + command: | + # Build from local master + git checkout master + git pull origin master + dotnet build Microsoft.Azure.Cosmos.sln -c Release + # Reference local build in test project +``` + +### 4.3 Reproduction Decision Matrix + +| Condition | Action | +|-----------|--------| +| Clear repro steps + code sample | Attempt full reproduction on all 3 versions | +| Partial repro steps | Create minimal test, request clarification | +| No repro steps, clear error | Attempt based on error pattern | +| Intermittent issue | Create stress test, log diagnostics | +| Environment-specific | Document, request more details | + +### 4.4 Reproduction Test Template + +```csharp +// File: tests/IssueRepro/Issue_{number}_Tests.cs + +/// +/// Reproduction tests for GitHub Issue #{number} +/// Run against: Reported version, Latest stable, Master branch +/// +[TestClass] +public class Issue_{number}_Tests +{ + private CosmosClient _client; + private Container _container; + + // Capture which version is being tested + private static readonly string SdkVersion = typeof(CosmosClient).Assembly + .GetCustomAttribute()?.InformationalVersion; + + [TestInitialize] + public async Task Setup() + { + Console.WriteLine($"Testing with SDK Version: {SdkVersion}"); + + // Setup based on issue details + _client = new CosmosClient( + connectionString: EmulatorConnectionString, + clientOptions: new CosmosClientOptions + { + ConnectionMode = ConnectionMode.{Direct|Gateway}, + // Add any specific options from issue + }); + + var database = await _client.CreateDatabaseIfNotExistsAsync("IssueReproDb"); + _container = await database.Database.CreateContainerIfNotExistsAsync( + new ContainerProperties("IssueReproContainer", "/pk")); + } + + [TestMethod] + [Description("Reproduction for GitHub Issue #{number}: {title}")] + public async Task Reproduce_Issue_{number}_GatewayMode() + { + // Test in Gateway mode + } + + [TestMethod] + [Description("Reproduction for GitHub Issue #{number}: {title}")] + public async Task Reproduce_Issue_{number}_DirectMode() + { + // Test in Direct mode + } + + [TestCleanup] + public async Task Cleanup() + { + await _client.GetDatabase("IssueReproDb").DeleteAsync(); + _client.Dispose(); + } +} +``` + +### 4.5 Running Multi-Version Reproductions + +```yaml +test_execution: + reported_version: + setup: | + cd IssueRepro_{number}_v{reported} + dotnet restore + gateway: + command: dotnet test --filter "Issue_{number}" -- CosmosDB:ConnectionMode=Gateway + direct: + command: dotnet test --filter "Issue_{number}" -- CosmosDB:ConnectionMode=Direct + record: "version={reported}, gateway={pass/fail}, direct={pass/fail}" + + latest_version: + setup: | + cd IssueRepro_{number}_latest + dotnet restore + gateway: + command: dotnet test --filter "Issue_{number}" -- CosmosDB:ConnectionMode=Gateway + direct: + command: dotnet test --filter "Issue_{number}" -- CosmosDB:ConnectionMode=Direct + record: "version=latest, gateway={pass/fail}, direct={pass/fail}" + + master_branch: + setup: | + # Ensure master is built + dotnet build Microsoft.Azure.Cosmos.sln -c Release + gateway: + command: dotnet test --filter "Issue_{number}" -- CosmosDB:ConnectionMode=Gateway + direct: + command: dotnet test --filter "Issue_{number}" -- CosmosDB:ConnectionMode=Direct + record: "version=master, gateway={pass/fail}, direct={pass/fail}" + +capture: + - CosmosDiagnostics output + - Exception details + - Request/response traces + - Timeline of operations + - SDK version in output +``` + +### 4.6 Reproduction Results Summary Template + +```markdown +## Reproduction Results + +| Version | Gateway Mode | Direct Mode | Notes | +|---------|--------------|-------------|-------| +| {reported_version} | ✅ Reproduced / ❌ Not Reproduced | ✅ / ❌ | {notes} | +| {latest_version} | ✅ / ❌ | ✅ / ❌ | {notes} | +| master ({commit_sha}) | ✅ / ❌ | ✅ / ❌ | {notes} | + +**Conclusion:** +- [ ] Issue persists in all versions → Needs fix +- [ ] Fixed in latest release → Recommend upgrade to {version} +- [ ] Fixed in master, not released → Will be available in next release +- [ ] Cannot reproduce → Need more information +``` + +### 4.7 Performance Issue Validation via Benchmarks + +For performance-related issues, use BenchmarkDotNet to validate and measure impact. + +#### When to Run Benchmarks +```yaml +benchmark_triggers: + - Issue mentions: "slow", "performance", "latency", "throughput", "RU", "timeout" + - Issue type: "Bug - Performance" + - Suspected hot path changes + - Before/after fix validation +``` + +#### Benchmark Project Setup + +```csharp +// File: tests/IssueRepro/Benchmarks/Issue_{number}_Benchmark.cs + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using Microsoft.Azure.Cosmos; + +[MemoryDiagnoser] +[RankColumn] +[MinColumn, MaxColumn, MeanColumn, MedianColumn] +public class Issue_{number}_Benchmark +{ + private CosmosClient _client; + private Container _container; + + [Params("Gateway", "Direct")] + public string ConnectionMode { get; set; } + + [Params("3.35.0", "Latest", "Master")] // Versions to compare + public string SdkVersion { get; set; } + + [GlobalSetup] + public async Task Setup() + { + var options = new CosmosClientOptions + { + ConnectionMode = ConnectionMode == "Direct" + ? Microsoft.Azure.Cosmos.ConnectionMode.Direct + : Microsoft.Azure.Cosmos.ConnectionMode.Gateway + }; + + _client = new CosmosClient(EmulatorConnectionString, options); + var db = await _client.CreateDatabaseIfNotExistsAsync("BenchmarkDb"); + _container = await db.Database.CreateContainerIfNotExistsAsync( + new ContainerProperties("BenchmarkContainer", "/pk")); + + // Warm up + await WarmupAsync(); + } + + [GlobalCleanup] + public async Task Cleanup() + { + await _client.GetDatabase("BenchmarkDb").DeleteAsync(); + _client.Dispose(); + } + + [Benchmark(Baseline = true)] + public async Task Baseline_Operation() + { + // Baseline operation for comparison + } + + [Benchmark] + public async Task Issue_{number}_Scenario() + { + // The specific operation reported as slow + } + + private async Task WarmupAsync() + { + // Warmup to stabilize connections + for (int i = 0; i < 10; i++) + { + await _container.ReadContainerAsync(); + } + } +} + +// Runner +public class Program +{ + public static void Main(string[] args) + { + var summary = BenchmarkRunner.Run(); + } +} +``` + +#### Benchmark Execution + +```yaml +benchmark_workflow: + setup: + - Install BenchmarkDotNet: dotnet add package BenchmarkDotNet + - Ensure Release build: dotnet build -c Release + - Start emulator with consistent state + + execution: + command: dotnet run -c Release --project Benchmarks/Issue_{number}_Benchmark.csproj + + compare_versions: + # Run same benchmark against different SDK versions + reported_version: + command: dotnet run -c Release -- --filter "*" --artifacts ./results/v{reported} + latest_version: + command: dotnet run -c Release -- --filter "*" --artifacts ./results/latest + master_branch: + command: dotnet run -c Release -- --filter "*" --artifacts ./results/master +``` + +#### Benchmark Metrics to Capture + +```yaml +metrics: + latency: + - Mean (ms) + - Median (ms) + - P95 (ms) + - P99 (ms) + - Min/Max (ms) + + throughput: + - Operations/second + - RU/s consumed + + memory: + - Allocated bytes + - Gen0/Gen1/Gen2 collections + + comparison: + - Ratio vs baseline + - Ratio vs previous version +``` + +#### Benchmark Results Template + +```markdown +## Performance Benchmark Results + +**Issue:** #{number} - {title} +**Scenario:** {description of operation benchmarked} +**Environment:** {CPU, RAM, OS, Emulator version} + +### Latency Comparison + +| Version | Connection | Mean | Median | P95 | P99 | Allocated | +|---------|------------|------|--------|-----|-----|-----------| +| {reported} | Gateway | {ms} | {ms} | {ms} | {ms} | {KB} | +| {reported} | Direct | {ms} | {ms} | {ms} | {ms} | {KB} | +| {latest} | Gateway | {ms} | {ms} | {ms} | {ms} | {KB} | +| {latest} | Direct | {ms} | {ms} | {ms} | {ms} | {KB} | +| master | Gateway | {ms} | {ms} | {ms} | {ms} | {KB} | +| master | Direct | {ms} | {ms} | {ms} | {ms} | {KB} | + +### Throughput Comparison + +| Version | Connection | Ops/sec | RU/s | RU/op | +|---------|------------|---------|------|-------| +| ... | ... | ... | ... | ... | + +### Analysis + +**Regression Detected:** Yes/No +**Regression Severity:** {X}% slower than baseline +**Root Cause Hypothesis:** {explanation} + +### Benchmark Artifacts +- Full BenchmarkDotNet report: [link to artifacts] +- Flame graph (if applicable): [link] +``` + +#### Performance Acceptance Criteria + +```yaml +acceptance_criteria: + no_regression: + latency_increase: "< 5% vs baseline" + memory_increase: "< 10% vs baseline" + throughput_decrease: "< 5% vs baseline" + + fix_validation: + must_show: "Measurable improvement in reported scenario" + no_side_effects: "No regression in other operations" + + documentation: + required: "Before/after benchmark comparison in PR" +``` + +### 4.8.1 Local Testing Strategy (Emulator-First) + +**⚠️ CRITICAL: Run maximum tests locally BEFORE pushing to CI.** + +Most tests can run locally with the Cosmos DB Emulator. Only a few tests require Azure secrets. + +```yaml +local_testing_strategy: + principle: "Validate locally first, CI is final verification" + + test_categories_by_locality: + fully_local: + unit_tests: + path: "Microsoft.Azure.Cosmos.Tests" + command: "dotnet test" + secrets_required: false + external_dependencies: false + note: "Pure unit tests - no emulator, no network, no secrets" + run: "ALWAYS before push" + + emulator_tests: + path: "Microsoft.Azure.Cosmos.EmulatorTests" + command: "dotnet test" + secrets_required: false + prerequisite: "Cosmos DB Emulator running" + run: "ALWAYS before push" + categories: + - "Query tests" + - "CRUD operations" + - "ChangeFeed tests" + - "Batch operations" + - "LINQ tests" + + encryption_tests: + path: "Microsoft.Azure.Cosmos.Encryption.Tests" + command: "dotnet test" + secrets_required: false + run: "If encryption code changed" + + requires_secrets: + multi_region_tests: + category: "MultiRegion" + secret: "COSMOSDB_MULTI_REGION" + run: "CI only (or with personal Azure account)" + + live_account_tests: + category: "LiveTest" + secret: "COSMOSDB_ACCOUNT_*" + run: "CI only" + + emulator_setup: + windows: + install: | + # Download from Azure portal or use winget + winget install Microsoft.Azure.CosmosEmulator + + start: | + # Start emulator + & "C:\Program Files\Azure Cosmos DB Emulator\CosmosDB.Emulator.exe" + + verify: | + # Check emulator is running + Invoke-WebRequest -Uri "https://localhost:8081/_explorer/emulator.pem" -UseBasicParsing + + connection_string: "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" + + recommended_workflow: + before_any_push: + step_1: + name: "Start emulator" + command: "Start-Process 'C:\\Program Files\\Azure Cosmos DB Emulator\\CosmosDB.Emulator.exe'" + wait: "30 seconds for startup" + + step_2: + name: "Run unit tests" + command: "dotnet test Microsoft.Azure.Cosmos.Tests -c Release" + expected: "All pass" + time: "~2 minutes" + note: "No emulator needed - pure unit tests" + + step_3: + name: "Run emulator tests (Pipeline 1 - Query, ReadFeed, Batch, ChangeFeed)" + command: | + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests -c Release --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory!=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed) & TestCategory!=Ignore" --verbosity normal + expected: "All pass" + time: "~10-15 minutes" + source: "templates/build-test.yml:EmulatorPipeline1Arguments" + + step_4: + name: "Run emulator tests (Pipeline 2 - Others)" + command: | + dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests -c Release --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory!=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & TestCategory!=Ignore" --verbosity normal + expected: "All pass" + time: "~10-15 minutes" + source: "templates/build-test.yml:EmulatorPipeline2Arguments" + note: "MultiRegion and MultiMaster tests require Azure secrets - CI only" + + step_5: + name: "Run specific area tests" + example: "dotnet test --filter \"FullyQualifiedName~Linq\" -c Release" + when: "For targeted validation of specific area" + + step_6: + name: "Push to CI" + action: "git push" + note: "CI runs secrets-required tests (MultiRegion, MultiMaster)" + + # These are the EXACT filter arguments used in CI pipelines (templates/build-test.yml) + # Use these to ensure local runs match CI behavior + ci_exact_arguments: + source_file: "templates/build-test.yml" + + emulator_pipeline_1: + name: "Query, ChangeFeed, ReadFeed, Batch" + filter: '--filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory!=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed) & TestCategory!=Ignore" --verbosity normal' + + emulator_pipeline_2: + name: "Others (excluding Pipeline 1 categories)" + filter: '--filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory!=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & TestCategory!=Ignore" --verbosity normal' + + emulator_pipeline_3: + name: "MultiRegion" + filter: '--filter "TestCategory=MultiRegion" --verbosity normal' + note: "Requires COSMOSDB_MULTI_REGION secret - CI only" + + emulator_pipeline_4: + name: "MultiMaster" + filter: '--filter "TestCategory=MultiMaster" --verbosity normal' + note: "Requires secrets - CI only" + + flaky_tests: + filter: '--filter "TestCategory=Flaky" --verbosity normal' + note: "Run separately, allowed to fail" + + test_coverage_by_environment: + local_no_dependencies: + project: "Microsoft.Azure.Cosmos.Tests" + coverage: "Unit tests - no external dependencies" + includes: + - "All unit tests" + - "Mocked integration tests" + - "Serialization tests" + - "Type system tests" + - "LINQ translation tests" + requires: "Nothing - just .NET SDK" + time: "~2 minutes" + + local_with_emulator: + project: "Microsoft.Azure.Cosmos.EmulatorTests" + coverage: "Integration tests with real Cosmos operations" + includes: + - "CRUD operations" + - "Query execution" + - "ChangeFeed tests" + - "Batch tests" + - "Encryption tests" + requires: "Cosmos DB Emulator running" + time: "~15-30 minutes" + + ci_only: + coverage: "~15% of all tests" + includes: + - "Multi-region replication" + - "Live account integration" + - "Cross-region failover" + - "Production endpoint tests" + requires: "Azure secrets" + + efficiency_gains: + unit_tests_only: "Catch most logic errors in ~2 minutes (no setup)" + with_emulator: "Catch integration issues in ~20 minutes" + ci_feedback: "Avoid 60-90 minute CI wait for simple errors" + iteration_speed: "Fix → unit test → fix → emulator test → push" +``` + +**Quick Local Test Commands:** + +```powershell +# Start emulator (if not running) +Start-Process "C:\Program Files\Azure Cosmos DB Emulator\CosmosDB.Emulator.exe" + +# Run all unit tests (~2 min) +dotnet test .\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests -c Release + +# Run emulator tests (~20 min) +dotnet test .\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.EmulatorTests -c Release + +# Run specific test category +dotnet test --filter "TestCategory=Query" -c Release + +# Run tests matching name pattern +dotnet test --filter "FullyQualifiedName~Dictionary" -c Release + +# Run single test +dotnet test --filter "FullyQualifiedName=Microsoft.Azure.Cosmos.Tests.Linq.LinqDictionaryQueryTests.TestIsDictionaryExtension" -c Release +``` + +### 4.8 Regression Testing Requirement + +**Before any fix is considered complete, ALL existing tests must pass - both locally AND on remote CI.** + +```yaml +regression_testing: + required: true + + local_validation: + description: "Quick local checks before PR" + workflow: + step_1: + description: "Run full test suite before making changes" + command: dotnet test Microsoft.Azure.Cosmos.sln --no-build -c Release + purpose: "Establish baseline - all tests must pass" + + step_2: + description: "Run full test suite after making changes" + command: dotnet test Microsoft.Azure.Cosmos.sln --no-build -c Release + purpose: "Verify no regressions introduced" + + step_3: + description: "Compare test results" + criteria: + - No previously passing tests should fail + - New tests should pass + - No increase in skipped tests + + remote_ci_validation: + description: "Full CI gate validation (REQUIRED)" + reference: "See Section 7.4 for full CI workflow" + gates_that_must_pass: + - static-tools (code analysis) + - nuget-pack (package build) + - build-test (unit + integration tests) + - build-samples (sample compilation) + - build-benchmark (benchmark build) + - build-preview (preview builds) + - build-internal (internal builds) + - build-thinclient (thin client builds) + note: "Local tests may pass but remote CI catches additional issues" + + test_categories: + unit_tests: + path: "Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests" + command: "dotnet test" + required: true + local: true + external_dependencies: false + note: "No emulator, no network, no secrets - pure unit tests" + remote: true + + emulator_tests: + path: "Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests" + command: dotnet test + required: "when emulator available" + local: "if emulator installed" + remote: true # CI has automated emulator setup + + multi_region_tests: + path: "Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests" + filter: "TestCategory=MultiRegion" + required: "for cross-region changes" + local: false # Requires Azure resources + remote: true # Uses COSMOSDB_MULTI_REGION secret + + performance_tests: + path: "Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests" + command: dotnet run -c Release + required: "for performance-related changes" + local: true + remote: true + + failure_handling: + if_baseline_fails: + - Document which tests were already failing + - Do not include those in regression comparison + - Note pre-existing failures in investigation issue + + if_new_failures: + - Stop and investigate + - Do not proceed with PR until resolved + - Document root cause of regression + + if_ci_only_failure: + - Get logs using github-mcp-server-get_job_logs + - Check if failure is infrastructure (emulator, network) + - Check if failure is environment-specific + - May need to re-run pipeline for transient failures + + pr_checklist: + - "[ ] Build succeeds: dotnet build -c Release" + - "[ ] Unit tests pass locally (SHOW OUTPUT)" + - "[ ] Emulator tests pass locally (if applicable, SHOW OUTPUT)" + - "[ ] No regression in existing tests" + - "[ ] New tests added for the fix" + - "[ ] **PROOF OF LOCAL TESTS SHOWN** (see Section 7.4.3)" + - "[ ] PR created and CI triggered" + - "[ ] ALL remote CI gates pass (Section 7.4)" + - "[ ] CI failures investigated and resolved" +``` + +--- + +When creating a linked investigation issue: + +```markdown +# Investigation: #{original_issue_number} - {title} + +## Original Issue +Linked to: #{original_issue_number} +Reporter: @{username} +Created: {date} + +## Issue Summary +{2-3 sentence summary of the reported problem} + +## Environment Details +| Property | Value | +|----------|-------| +| SDK Version | {version} | +| .NET Version | {version} | +| Connection Mode | {Direct/Gateway} | +| Operation Type | {CRUD/Query/Batch/etc} | +| Account Type | {Serverless/Provisioned} | + +## Reproduction Status +- [ ] Reproduced with reported SDK version ({version}) +- [ ] Reproduced with latest SDK version ({version}) +- [ ] Reproduced with master branch ({commit_sha}) +- [ ] Reproduced in Gateway mode +- [ ] Reproduced in Direct mode +- [ ] Unable to reproduce +- [ ] Needs more information + +### Version Matrix Results + +| Version | Gateway | Direct | Build | Notes | +|---------|---------|--------|-------|-------| +| {reported_version} | ✅/❌ | ✅/❌ | ✅ | {notes} | +| {latest_version} | ✅/❌ | ✅/❌ | ✅ | {notes} | +| master ({short_sha}) | ✅/❌ | ✅/❌ | ✅ | {notes} | + +### Reproduction Steps Attempted +~~~ +{steps taken} +~~~ + +### Reproduction Results +~~~ +{output/logs} +~~~ + +## Code Analysis + +### Suspected Code Path +~~~ +{file}:{line} - {function_name} + └─> {file}:{line} - {called_function} + └─> {file}:{line} - {root_cause_location} +~~~ + +### Relevant Code Sections +~~~csharp +// {file}:{start_line}-{end_line} +{code snippet} +~~~ + +### Root Cause Analysis +{detailed explanation of what's happening and why} + +## Historical Context + +### Related StackOverflow / External References +- [{title}]({url}) - {relevance summary} +- [{title}]({url}) - {relevance summary} + +### Related Issues +- #{issue_number} - {title} ({status}) +- #{issue_number} - {title} ({status}) + +### Related PRs/Fixes +- PR #{pr_number} - {title} (merged {date}) + +### Changelog References +- Version {x.y.z}: {relevant changelog entry} + +## Workaround +{If available, provide a workaround} + +~~~csharp +// Workaround code example +~~~ + +## Proposed Fix + +### Option 1: {approach name} +**Pros:** +**Cons:** +**Risk:** Low/Medium/High +**Effort:** Small/Medium/Large + +~~~csharp +// Proposed code change +~~~ + +### Option 2: {alternative approach} +... + +## Recommended Action +- [ ] Needs more investigation +- [ ] Ready for PR (recommend Option {n}) +- [ ] Won't fix (reason: {reason}) +- [ ] Duplicate of #{issue_number} + +## Reviewer Assignment +Based on area labels, assign to: @{reviewer} + +/cc @{relevant_team_members} +``` + +--- + +## 5. Investigation Issue Creation + +> **Note:** Section 5 covers creating investigation issues for complex bugs that require detailed tracking. The investigation issue template is detailed in Section 4's "Investigation Issue Template" subsection. + +### 5.1 When to Create Investigation Issue + +```yaml +create_investigation_issue_when: + - Root cause requires deep analysis (> 30 min) + - Multiple code paths involved + - Reproduction is complex or intermittent + - Fix requires design discussion + - Historical context needs documentation + +skip_investigation_issue_when: + - Simple bug with obvious fix + - Question that can be answered directly + - Documentation-only change + - Already has linked investigation issue +``` + +### 5.2 Investigation Issue Linking + +```yaml +linking_strategy: + original_issue: + add_comment: "🔍 Investigation started - see #{investigation_issue_number}" + add_label: "investigating" + + investigation_issue: + title: "Investigation: #{original_number} - {title}" + link_to_original: "Linked to: #{original_number}" + add_labels: ["investigation", "{area}"] +``` + +--- + +## 6. Workaround Identification + +### 6.1 Common Workaround Patterns + +| Issue Pattern | Potential Workaround | +|--------------|---------------------| +| Timeout in Direct mode | Switch to Gateway mode temporarily | +| Serialization failure | Use custom serializer with explicit handling | +| Retry exhaustion | Increase MaxRetryAttempts, add jitter | +| Partition key error | Explicit partition key in request options | +| Bulk throttling | Reduce batch size, implement backoff | +| Connection issues | Configure IdleTcpConnectionTimeout | +| Memory pressure | Enable streaming APIs, dispose properly | + +### 6.2 Workaround Documentation Template + +```markdown +## Temporary Workaround + +**Applies to:** SDK versions {range} +**Issue:** {brief description} + +### Option 1: Configuration Change +~~~csharp +var options = new CosmosClientOptions +{ + // Workaround configuration +}; +~~~ + +### Option 2: Code Pattern +~~~csharp +// Workaround code pattern +~~~ + +### Limitations +- {limitation 1} +- {limitation 2} + +### When Fix is Available +This workaround can be removed when upgrading to SDK version {x.y.z} or later. +``` + +--- + +## 7. PR Creation Workflow + +### 7.1 Branch Naming Convention + +**Format:** `users//copilot--` + +```yaml +branch_naming: + pattern: "users/{username}/{type}-{description}" + + types: + - fix # Bug fixes + - feature # New features + - perf # Performance improvements + - docs # Documentation changes + - refactor # Code refactoring + + examples: + - "users/kirankk/copilot-5547-fix-linq-dictionary-objecttoarray" + - "users/kirankk/copilot-1234-feature-bulk-retry" + - "users/johndoe/copilot-5678-perf-batch-throughput" + - "users/janedoe/copilot-9999-docs-linq-dictionary" + + rules: + - Use lowercase + - Use hyphens (not underscores) as separators + - Always include `copilot-` prefix for Copilot-authored branches + - Always include issue number after `copilot-` + - Keep feature description concise but descriptive + - Username should match GitHub handle +``` + +### 7.2 PR Eligibility Criteria + +```yaml +create_pr_when: + - Root cause identified AND + - Fix verified in reproduction tests AND + - No breaking changes OR approved breaking change AND + - Follows existing code patterns AND + - Human reviewer identified + +do_not_create_pr_when: + - Requires design discussion + - Breaking change without approval + - Unclear requirements + - Complex cross-cutting change + - Security-sensitive fix (escalate instead) +``` + +### 7.3 PR Template + +**PR must include full investigation details, not just a summary.** + +```markdown +# {Fix type}: {Brief description} + +## Description +Fixes #{issue_number} + +> 🤖 **This PR was authored by GitHub Copilot** as part of an automated issue triage and resolution workflow. + +{Detailed description of the problem and the fix} + +--- + +## Issue Summary +| Property | Value | +|----------|-------| +| Issue | #{issue_number} | +| Area | {Query/Batch/ChangeFeed/etc} | +| SDK Version (reported) | {version} | +| Severity | {P0-P3} | + +--- + +## Root Cause Analysis + +### Code Path +~~~ +{file1}:{line} - {function_name} + └─> {file2}:{line} - {called_function} + └─> {file3}:{line} - {root_cause_location} +~~~ + +### Root Cause +{Detailed explanation of why the bug existed, including: +- What the code was doing incorrectly +- Why this caused the reported behavior +- Any historical context (was this always broken, or a regression?)} + +--- + +## Changes Made + +### Files Modified +| File | Change | +|------|--------| +| `{file1}` | {description of change} | +| `{file2}` | {description of change} | + +### Code Changes +{Brief description of each change and why it fixes the issue} + +--- + +## Generated Output (Before/After) + +**Before (incorrect):** +~~~ +{output/SQL/JSON showing the bug} +~~~ + +**After (correct):** +~~~ +{output/SQL/JSON showing correct behavior} +~~~ + +--- + +## Workaround (For Users Not Yet Upgraded) + +{If a workaround exists, document it here so users can unblock themselves} + +~~~csharp +// Workaround code example +~~~ + +--- + +## Testing + +### Test Results +| Test Suite | Total | Passed | Failed | +|------------|-------|--------|--------| +| {test_suite_1} | {n} | {n} | {n} | +| {test_suite_2} | {n} | {n} | {n} | +| Build | - | ✅ | - | + +### New Tests Added +- `{TestClass}.{TestMethod}` - {description} + +### Reproduction Test +~~~csharp +// Test that reproduces the original issue and verifies the fix +[TestMethod] +public void Issue_{number}_Reproduction() +{ + // Arrange - setup that triggers the bug + // Act - operation that was failing + // Assert - verify correct behavior +} +~~~ + +--- + +## Breaking Changes +{None | Description of breaking changes and migration guide} + +--- + +## External References +- StackOverflow: {link if applicable} +- Microsoft Docs: {link if applicable} +- Related Issues: #{related_issue_numbers} + +--- + +## Checklist +- [ ] Code follows project conventions +- [ ] Self-review completed +- [ ] Comments added for complex logic +- [ ] Documentation updated (if applicable) +- [ ] New tests added for the fix +- [ ] All existing tests pass +- [ ] Remote CI gates pass (Section 7.4) + +--- + +*Generated by GitHub Copilot CLI Agent* +``` + +### 7.3.1 PR Reference Guidelines + +**Always include proper references between PRs and Issues:** + +```yaml +pr_references: + in_pr_description: + required: + - "Fixes #{issue_number}" (auto-closes issue on merge) + - Issue summary table with link + optional: + - Related issues: "Related to #{number}" + - Depends on: "Depends on #{pr_number}" + + in_issue_comments: + when_pr_created: | + Add comment to original issue: + "🤖 **Copilot Investigation Complete** + + **PR:** #{pr_number} - {pr_title} + **Branch:** `{branch_name}` + **Status:** Awaiting CI validation + + See PR for full root cause analysis and fix details." + + in_investigation_issue: + if_created: | + Update with PR link: + "### PR Reference + **PR:** #{pr_number} - {pr_title} + **Branch:** `{branch_name}` + **CI Status:** {Pending/Passing/Failing}" + +branch_to_pr_url: + pattern: "https://github.com/{owner}/{repo}/pull/new/{branch_name}" + example: "https://github.com/Azure/azure-cosmos-dotnet-v3/pull/new/users/kirankk/copilot-5547-fix-linq-dictionary" +``` + +### 7.4 Reviewer Assignment Matrix + +| Area Label | Primary Reviewer | Backup | +|------------|-----------------|--------| +| Batch | @batch-owners | @sdk-team | +| Query | @query-owners | @sdk-team | +| ChangeFeed | @changefeed-owners | @sdk-team | +| DirectMode | @transport-owners | @sdk-team | +| Serialization | @serialization-owners | @sdk-team | +| Encryption | @encryption-owners | @security-team | + +### 7.5 Remote CI Validation (Azure Pipelines Gates) + +**Critical: Local tests are not sufficient. All fixes must pass the full Azure Pipelines CI gates.** + +#### 7.5.1 CI Pipeline Structure + +The repository uses Azure Pipelines with multiple gate templates defined in `azure-pipelines.yml`: + +```yaml +ci_gates: + # All gates must pass before PR can be merged + + static_tools: + template: templates/static-tools.yml + checks: + - Code analysis + - Style compliance + - Security scanning + + nuget_pack: + template: templates/nuget-pack.yml + checks: + - Package builds successfully + - No packaging errors + + build_test: + template: templates/build-test.yml + checks: + - Unit tests (Release config) + - Integration tests + - Multi-region tests (if applicable) + filter: "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=Ignore" + + build_samples: + template: templates/build-samples.yml + checks: + - All samples compile + - Samples reference correct SDK version + + build_benchmark: + template: templates/build-benchmark.yml + checks: + - Benchmark project builds + - No performance regression detected + + build_preview: + template: templates/build-preview.yml + checks: + - Preview features compile + - PREVIEW define constant works + + build_internal: + template: templates/build-internal.yml + checks: + - Internal builds succeed + + build_thinclient: + template: templates/build-thinclient.yml + checks: + - Thin client variant builds +``` + +#### 7.5.2 Local vs Remote CI Comparison + +| Validation Type | Local Testing | Remote CI (Azure Pipelines) | +|----------------|---------------|----------------------------| +| **Unit Tests** | ✅ Can run | ✅ Full matrix | +| **Emulator Tests** | ⚠️ Requires local emulator | ✅ Automated emulator setup | +| **Multi-Region Tests** | ❌ No access | ✅ Uses COSMOSDB_MULTI_REGION | +| **Multi-Master Tests** | ❌ No access | ✅ Uses COSMOSDB_MULTIMASTER | +| **Static Analysis** | ⚠️ Manual | ✅ Automated | +| **Package Validation** | ⚠️ Manual | ✅ NuGet pack verification | +| **Cross-Platform** | ⚠️ Single OS | ✅ Windows matrix | +| **Performance Gates** | ⚠️ Variable hardware | ✅ Consistent CI agents | + +#### 7.5.3 Pre-PR Validation Workflow + +```yaml +validation_workflow: + phase_1_local: + description: "Local validation with PROOF before creating PR" + + build_configurations: + default: + command: "dotnet build Microsoft.Azure.Cosmos.sln -c Release" + defines: "(none)" + required: true + + preview: + command: "dotnet build Microsoft.Azure.Cosmos.sln -c Release /p:IsPreview=true" + defines: "PREVIEW;ENCRYPTIONPREVIEW" + required: true + note: "Tests preview-only APIs and code paths" + + test_configurations: + default: + build: "dotnet build -c Release" + test: "dotnet test -c Release --no-build" + required: true + + preview: + build: "dotnet build -c Release /p:IsPreview=true" + test: "dotnet test -c Release --no-build" + required: true + note: "Must pass with PREVIEW defined" + + steps: + - name: "Build solution (default)" + command: "dotnet build Microsoft.Azure.Cosmos.sln -c Release" + required: true + + - name: "Run unit tests (default)" + command: "dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests -c Release --no-build" + required: true + + - name: "Build solution (PREVIEW)" + command: "dotnet build Microsoft.Azure.Cosmos.sln -c Release /p:IsPreview=true" + required: true + + - name: "Run unit tests (PREVIEW)" + command: "dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests -c Release --no-build" + required: true + note: "Tests run against PREVIEW build" + + - name: "Run emulator tests" + command: "dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests -c Release" + required: true + note: "Requires Cosmos DB emulator running locally" + + proof_required: + description: "⚠️ MUST show test output for BOTH configurations before creating PR" + format: | + ## Local Test Results + + ### Unit Tests (Default) + ``` + Passed: XXX + Failed: 0 + Skipped: X + ``` + + ### Unit Tests (PREVIEW) + ``` + Passed: XXX + Failed: 0 + Skipped: X + ``` + + ### Emulator Tests + ``` + Passed: XXX + Failed: 0 + Skipped: X + ``` + + ### Emulator Tests (if run) + ``` + Passed: XXX + Failed: 0 + ``` + + gate: "DO NOT create PR if any tests fail in ANY configuration" + + phase_2_code_review: + prerequisite: "phase_1_local MUST pass" + description: "Run Copilot code review BEFORE creating PR" + + purpose: | + Catch issues before human reviewers see them: + - Bugs and logic errors + - Security vulnerabilities + - Missing error handling + - Performance issues + + steps: + - name: "Run Copilot code-review agent" + agent: "code-review" + scope: "Staged/uncommitted changes" + prompt: | + Review the changes for this fix. Focus on: + - Correctness of the fix + - Edge cases not handled + - Security implications + - Performance impact + + - name: "Address findings" + action: | + For each issue found: + 1. Evaluate if it's valid + 2. Fix valid issues locally + 3. Re-run local tests + 4. Re-run code review until clean + + gate: "DO NOT create PR until code review passes with no high-signal issues" + + acceptable_to_ignore: + - "Style/formatting suggestions (StyleCop handles this)" + - "Minor refactoring suggestions" + - "Documentation improvements (unless critical)" + + must_address: + - "Bugs or logic errors" + - "Security vulnerabilities" + - "Null reference risks" + - "Resource leaks" + - "Missing error handling" + + phase_3_create_pr: + prerequisite: "phase_1_local AND phase_2_code_review MUST pass" + description: "Create PR to trigger remote CI" + steps: + - name: "Create feature branch" + naming_convention: "users//copilot--" + examples: + - "users/kirankk/copilot-5547-fix-linq-dictionary" + - "users/johndoe/copilot-1234-perf-batch-throughput" + command: | + git checkout -b users/{username}/copilot-{number}-{short-description} + git add . + git commit -m "Fix #{number}: {description}" + git push origin users/{username}/copilot-{number}-{short-description} + + - name: "Create Draft PR" + purpose: "Triggers CI without requesting review" + template: | + gh pr create --draft \ + --title "Fix #{number}: {title}" \ + --body "$(cat pr_body.md)" + + phase_4_ci_monitoring: + description: "Monitor CI pipeline execution" + steps: + - name: "Check pipeline status" + tool: github-mcp-server-actions_list + params: + method: list_workflow_runs + workflow_runs_filter: + branch: "users/{username}/fix-issue-{number}" + status: "in_progress" + + - name: "Get failed job logs" + tool: github-mcp-server-get_job_logs + params: + failed_only: true + return_content: true + tail_lines: 500 + + phase_5_fix_ci_failures: + description: "Iterate until CI passes" + loop: + - Analyze failure logs + - Identify root cause of CI failure + - Fix locally + - "**FULL LOCAL VALIDATION BEFORE PUSH** (see 7.4.4.1)" + - Push fix + - Wait for CI re-run + exit_condition: "All CI gates green" +``` + +#### 7.5.4 CI Failure Triage + +**⚠️ CRITICAL: After fixing any CI failure, FULL local validation is required BEFORE pushing.** + +```yaml +pre_push_validation: + principle: "Never push a fix without verifying it locally first" + + required_before_every_push: + step_1_build: + command: "dotnet build Microsoft.Azure.Cosmos.sln -c Release" + must_show: "Build succeeded, 0 errors" + + step_2_unit_tests: + command: "dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests -c Release --no-build" + or_targeted: "dotnet test ... --filter 'FullyQualifiedName~{area}'" + must_show: "Passed: X, Failed: 0" + + step_3_affected_tests: + description: "Run tests in the area you changed" + examples: + linq_change: "--filter 'FullyQualifiedName~Linq'" + query_change: "--filter 'TestCategory=Query'" + + baseline_test_failures: + symptom: "Please run UpdateContracts.ps1 to update the baselines" + + fix_workflow: + step_1: "Run the specific failing test to generate new baseline" + step_2: "Copy output to baseline file" + step_3: "**RE-RUN THE TEST to verify baseline is correct**" + step_4: "Run related tests to verify no regressions" + step_5: "ONLY THEN commit and push" + + common_mistake: "Pushing baseline update without re-running tests" + why_bad: "Baseline may be incomplete or test may still fail" + + correct_sequence: | + # 1. Run failing test (generates output) + dotnet test ... --filter "FullyQualifiedName~{FailingTest}" + + # 2. Copy output to baseline + Copy-Item -Path "{output_path}" -Destination "{baseline_path}" + + # 3. RE-RUN to verify (MUST PASS) + dotnet test ... --filter "FullyQualifiedName~{FailingTest}" --no-build + + # 4. Run related tests + dotnet test ... --filter "FullyQualifiedName~{Area}" --no-build + + # 5. NOW commit and push + git add {baseline_file} + git commit -m "..." + git push + +common_ci_failures: + build_failure: + symptoms: + - "error CS####" + - "Build FAILED" + actions: + - Check if failure is in changed files + - Verify all project references + - Check for missing using statements + + test_failure: + symptoms: + - "Failed: X, Passed: Y" + - "Assert.Xxx failed" + actions: + - Get failed test names from logs + - Check if test was passing before changes + - Reproduce failure locally if possible + - Check for flaky test (retry) + + emulator_failure: + symptoms: + - "Connection refused" + - "ServiceUnavailable" + - "Emulator not started" + actions: + - Check templates/emulator-setup.yml for setup steps + - This is likely infrastructure, not code issue + - May need to re-run pipeline + + timeout_failure: + symptoms: + - "Job cancelled" + - "Exceeded timeout" + actions: + - Check if test has infinite loop + - Check for deadlock in async code + - May need to increase timeout or fix perf + + multi_region_failure: + symptoms: + - "COSMOSDB_MULTI_REGION" + - "Endpoint not found" + actions: + - This requires actual Azure resources + - Cannot reproduce locally + - Check if failure is in multi-region specific code +``` + +#### 7.5.5 CI Gate Checklist for PR + +```markdown +## CI Validation Checklist + +### Local Validation (Required before PR) +- [ ] `dotnet build Microsoft.Azure.Cosmos.sln -c Release` passes +- [ ] Unit tests pass with CI filter +- [ ] No new compiler warnings + +### Remote CI Gates (Must pass before merge) +- [ ] **static-tools** - Code analysis clean +- [ ] **nuget-pack** - Package builds successfully +- [ ] **build-test** - All tests pass + - [ ] Unit tests + - [ ] Integration tests + - [ ] Multi-region tests (if applicable) +- [ ] **build-samples** - Samples compile +- [ ] **build-benchmark** - Benchmarks build +- [ ] **build-preview** - Preview builds work +- [ ] **build-internal** - Internal builds work +- [ ] **build-thinclient** - Thin client builds + +### CI Failure Resolution +- [ ] All CI failures investigated +- [ ] No failures related to this change +- [ ] Any infrastructure failures documented + +### Final Status +- [ ] All CI gates GREEN +- [ ] PR ready for review (not draft) +``` + +#### 7.5.6 Monitoring Tools + +```yaml +ci_monitoring_tools: + list_workflows: + tool: github-mcp-server-actions_list + method: list_workflows + purpose: "See all CI workflows in repo" + + list_runs: + tool: github-mcp-server-actions_list + method: list_workflow_runs + purpose: "Check status of PR's CI runs" + filter_by: + - branch + - status (queued, in_progress, completed) + - event (pull_request) + + get_run_details: + tool: github-mcp-server-actions_get + method: get_workflow_run + purpose: "Get details of specific CI run" + + list_jobs: + tool: github-mcp-server-actions_list + method: list_workflow_jobs + purpose: "See individual jobs in a run" + + get_logs: + tool: github-mcp-server-get_job_logs + purpose: "Get failure logs for debugging" + params: + failed_only: true + return_content: true + tail_lines: 500 +``` + +#### 7.5.7 Accessing Azure DevOps CI Logs + +**Use Azure DevOps REST API with PAT (see Section 0.3.1 for setup).** + +```yaml +azure_devops_log_access: + primary_method: "REST API with PAT" + setup_reference: "Section 0.3.1" + + quick_workflow: + step_1: "Ensure ADO_PAT environment variable is set" + step_2: "Get build ID from PR checks URL" + step_3: "Fetch timeline to find failed task" + step_4: "Fetch log content for failed task" + + fallback_options: + - "Reproduce locally with same CI filter (Section 4.8.1)" + - "Ask user to check ADO directly" +``` + +**Quick Reference - Get Failed CI Logs:** + +```powershell +# Requires: $env:ADO_PAT set (see Section 0.3.1) +$headers = @{ Authorization = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$env:ADO_PAT")) } +$buildId = 59172 # From PR checks URL + +# Get failed tasks +$timeline = Invoke-RestMethod -Uri "https://dev.azure.com/cosmos-db-sdk-public/cosmos-db-sdk-public/_apis/build/builds/$buildId/timeline?api-version=7.0" -Headers $headers +$failed = $timeline.records | Where-Object { $_.result -eq "failed" } +$failed | Select-Object name, @{N='logId';E={$_.log.id}} + +# Get log content +$logId = $failed[0].log.id +Invoke-RestMethod -Uri "https://dev.azure.com/cosmos-db-sdk-public/cosmos-db-sdk-public/_apis/build/builds/$buildId/logs/$logId?api-version=7.0" -Headers $headers +``` + +**If PAT Not Available - Reproduce Locally:** + +```bash +# 1. Get failed stage name from: gh pr checks {pr_number} +# 2. Map stage to filter (templates/build-test.yml) +# 3. Run locally: +dotnet test Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests -c Release \ + --filter "{same_filter_as_ci}" --verbosity detailed +``` + +--- + +## 8. Agent Capabilities & Tools + +### 8.1 Model Selection + +**All agents use Claude Opus 4.5** for maximum reasoning quality: + +```yaml +task_tool_invocation: + explore_agent: + agent_type: "explore" + model: "claude-opus-4.5" + + task_agent: + agent_type: "task" + model: "claude-opus-4.5" + + general_purpose_agent: + agent_type: "general-purpose" + model: "claude-opus-4.5" + + code_review_agent: + agent_type: "code-review" + model: "claude-opus-4.5" +``` + +### 8.2 MCP Tools Available + +```yaml +github_mcp_server: + - search_issues: Find related issues + - search_pull_requests: Find related PRs + - list_issues: Browse open issues + - issue_read: Get issue details + - pull_request_read: Get PR details + - get_commit: View commit details + - get_file_contents: Read files from repo + - get_job_logs: Check CI failures + +bluebird_engineering_copilot: + - do_vector_search: Semantic code search + - do_fulltext_search: Keyword code search + - get_source_code: Retrieve source + - get_hierarchical_summary: Get code overview + - get_function_calling_functions: Find callers + - get_function_called_functions: Find callees + - get_class_or_struct_parent_types: Inheritance + - get_class_or_struct_member_functions: Methods +``` + +### 8.3 Local Tools + +```yaml +file_operations: + - view: Read files + - edit: Modify files + - create: Create new files + - grep: Search content + - glob: Find files + +shell_operations: + - powershell: Run commands + - dotnet build: Build solution + - dotnet test: Run tests + - git: Version control +``` + +--- + +## 9. Workflow State Machine + +``` +┌─────────────┐ +│ NEW_ISSUE │ +└──────┬──────┘ + │ + ▼ +┌─────────────────┐ ┌──────────────────┐ +│ NEEDS_TRIAGE │────▶│ NEEDS_INFO │ +└────────┬────────┘ └────────┬─────────┘ + │ │ + │ ◀─────────────────────┘ + ▼ +┌─────────────────┐ +│ INVESTIGATING │ +└────────┬────────┘ + │ + ┌────┴────┐ + ▼ ▼ +┌────────┐ ┌─────────────┐ +│ REPRO │ │ CANNOT_REPRO│ +└───┬────┘ └──────┬──────┘ + │ │ + ▼ ▼ +┌─────────────────────────┐ +│ ANALYSIS_COMPLETE │ +└───────────┬─────────────┘ + │ + ┌───────┼───────┬──────────┐ + ▼ ▼ ▼ ▼ +┌──────┐ ┌──────┐ ┌────────┐ ┌──────────┐ +│ PR │ │WONT │ │NEEDS │ │DUPLICATE │ +│READY │ │FIX │ │DESIGN │ │ │ +└──┬───┘ └──────┘ └────────┘ └──────────┘ + │ + ▼ +┌─────────────────┐ +│ PR_CREATED │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ UNDER_REVIEW │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ MERGED/CLOSED │ +└─────────────────┘ +``` + +--- + +## 10. Quality Checklist + +### Before Creating Investigation Issue +- [ ] Issue fully understood +- [ ] Historical search completed +- [ ] Code path identified +- [ ] Reproduction attempted (if applicable) +- [ ] Root cause hypothesis formed + +### Before Proposing Workaround +- [ ] Workaround verified to work +- [ ] Side effects documented +- [ ] Limitations clearly stated +- [ ] Code example provided + +### Before Creating PR +- [ ] Fix verified in tests +- [ ] No regression introduced +- [ ] Code follows conventions +- [ ] Documentation updated +- [ ] Reviewer assigned + +--- + +## 11. Escalation Criteria + +Escalate to human immediately when: +- Security vulnerability suspected +- Data loss/corruption confirmed +- Breaking change required +- Cross-team coordination needed +- Legal/compliance implications +- Customer escalation mentioned +- P0 priority issue + +--- + +## 12. Post-Resolution Follow-up + +### 12.1 After PR Merge + +```yaml +post_merge_actions: + update_original_issue: + add_comment: | + ✅ **Fix merged in PR #{pr_number}** + + The fix will be available in the next SDK release. + Version: {expected_version} (estimated) + + Workaround (until release): {workaround_if_any} + + update_investigation_issue: + if_exists: "Close with reference to merged PR" + + changelog: + check: "Verify fix is mentioned in changelog.md" +``` + +### 12.2 Customer Communication + +```yaml +communication_template: + fix_available: | + @{reporter} - Good news! This issue has been fixed in PR #{pr_number}. + + **When available:** Next SDK release ({version}) + **Workaround:** {workaround_description} + + Please let us know if you have any questions. +``` + +--- + +## 13. Documentation Improvement Workflow + +### 13.1 Microsoft Docs Feedback Loop + +When investigating issues, identify documentation gaps that contributed to the issue and suggest improvements. + +#### Triggers for Docs Suggestions +```yaml +suggest_docs_update_when: + - Issue caused by misunderstanding documented behavior + - Common question pattern (3+ similar issues) + - Undocumented edge case discovered + - Error message not explained in docs + - Missing code sample for common scenario + - Outdated information found + - Missing migration guidance between versions +``` + +#### Microsoft Docs Feedback Template + +```markdown +## Documentation Improvement Suggestion + +**Source Issue:** #{issue_number} +**Docs Page:** https://docs.microsoft.com/azure/cosmos-db/{page} +**Severity:** High/Medium/Low + +### Current State +{What the docs currently say or don't say} + +### Problem +{How this caused confusion or the reported issue} + +### Suggested Improvement +{Specific text/code to add or change} + +### Sample Code to Add +~~~csharp +// Example that would have prevented this issue +~~~ + +### Related Issues +- #{issue_number} - {X users affected} +- StackOverflow: {link} - {Y views} + +--- +**Action:** File issue at https://github.com/MicrosoftDocs/azure-docs or submit PR +``` + +#### Docs Areas to Monitor + +| SDK Area | Microsoft Docs Section | Common Gaps | +|----------|----------------------|-------------| +| Connection/Setup | /azure/cosmos-db/nosql/quickstart-dotnet | Connection string formats, emulator setup | +| CRUD Operations | /azure/cosmos-db/nosql/how-to-dotnet-create-item | Error handling, partial success | +| Query | /azure/cosmos-db/nosql/how-to-dotnet-query-items | Pagination, cross-partition queries | +| Batch | /azure/cosmos-db/nosql/how-to-dotnet-batch-operations | Size limits, atomicity guarantees | +| Change Feed | /azure/cosmos-db/nosql/change-feed-processor | Lease management, error recovery | +| Partitioning | /azure/cosmos-db/partitioning-overview | Hot partitions, synthetic keys | +| Performance | /azure/cosmos-db/nosql/performance-tips-dotnet-sdk-v3 | Direct vs Gateway, connection pooling | + +--- + +### 13.2 Code Documentation for AI Agents + +Identify and improve SDK code documentation to make it more AI-agent friendly. + +#### Why This Matters +```yaml +ai_agent_challenges: + - Cannot infer intent from variable names alone + - Needs explicit error condition documentation + - Benefits from examples in XML comments + - Requires clear parameter constraints + - Needs explicit nullability documentation + - Benefits from "when to use" guidance +``` + +#### Code Documentation Audit Checklist + +When investigating issues, audit related code for AI-friendliness: + +```markdown +- [ ] XML summary describes WHAT and WHY, not just WHAT +- [ ] Parameters have tags with constraints +- [ ] Return values documented including null/empty cases +- [ ] Exceptions documented with tags +- [ ] Code examples in tags for complex APIs +- [ ] section for edge cases and gotchas +- [ ] links to related methods +- [ ] Async methods document cancellation behavior +``` + +#### Before/After Examples + +**❌ Current (AI-Unfriendly):** +```csharp +/// +/// Creates an item. +/// +/// The item to create. +/// The response. +public Task> CreateItemAsync(T item); +``` + +**✅ Improved (AI-Friendly):** +```csharp +/// +/// Creates a new item in the container. The item must have a unique 'id' property +/// within its partition key. Use if the item may already exist. +/// +/// The type of item to create. Must be JSON-serializable. +/// +/// The item to create. Must contain: +/// - 'id' property (string, max 255 chars, unique within partition) +/// - Partition key property matching container's partition key path +/// Cannot be null. +/// +/// +/// The partition key value for the item. If null, SDK extracts from item using +/// the container's partition key path. Explicit value recommended for performance. +/// +/// +/// Optional request configuration. Common options: +/// - EnableContentResponseOnWrite: false to reduce response size (default: true) +/// - IfMatchEtag: for optimistic concurrency +/// +/// Token to cancel the operation. Safe to cancel; no partial writes. +/// +/// Response containing: +/// - Resource: The created item (null if EnableContentResponseOnWrite=false) +/// - StatusCode: 201 Created on success +/// - RequestCharge: RU cost of operation +/// - ETag: Version identifier for optimistic concurrency +/// +/// +/// StatusCode 400: Invalid item (missing id, invalid JSON, exceeds 2MB) +/// StatusCode 409: Item with same id already exists in partition +/// StatusCode 413: Item exceeds maximum size (2MB) +/// StatusCode 429: Rate limited - retry after response.RetryAfter +/// +/// +/// Basic usage: +/// +/// var item = new { id = "1", pk = "partitionA", name = "Example" }; +/// var response = await container.CreateItemAsync(item, new PartitionKey("partitionA")); +/// Console.WriteLine($"Created item, cost: {response.RequestCharge} RUs"); +/// +/// +/// +/// With optimistic concurrency: +/// +/// try { +/// var response = await container.CreateItemAsync(item); +/// } catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.Conflict) { +/// // Item already exists - use UpsertItemAsync or ReplaceItemAsync +/// } +/// +/// +/// +/// Performance tips: +/// - Use Direct connection mode for lower latency +/// - Set EnableContentResponseOnWrite=false if you don't need the response body +/// - Provide explicit PartitionKey to avoid server-side extraction +/// +/// Size limits: +/// - Maximum item size: 2MB +/// - Maximum id length: 255 characters +/// - Maximum partition key value: 2KB +/// +/// +/// +public Task> CreateItemAsync( + T item, + PartitionKey? partitionKey = null, + ItemRequestOptions? requestOptions = null, + CancellationToken cancellationToken = default); +``` + +#### Priority Areas for Documentation Improvement + +Based on common issue patterns, prioritize these areas: + +| Priority | Class/Method | Why | +|----------|-------------|-----| +| P0 | `CosmosClient` constructor | Connection setup confusion | +| P0 | `Container.CreateItemAsync` | Most common operation | +| P0 | `CosmosException` | Error handling critical | +| P1 | `TransactionalBatch` | Complex API, many gotchas | +| P1 | `Container.GetItemQueryIterator` | Pagination confusion | +| P1 | `ChangeFeedProcessor` | Lifecycle management | +| P2 | `CosmosClientOptions` | Many options, unclear defaults | +| P2 | `ItemRequestOptions` | Conditional operations | +| P2 | `QueryDefinition` | Parameterized queries | + +#### Documentation PR Template + +```markdown +# Docs: Improve XML documentation for {ClassName} + +## Motivation +Issue #{number} revealed that the current documentation for `{ClassName}` +is insufficient for AI agents and developers to use correctly. + +## Changes +- Added detailed parameter constraints +- Added exception documentation with status codes +- Added code examples for common scenarios +- Added remarks for edge cases and performance tips +- Added seealso links to related APIs + +## AI-Agent Friendliness Checklist +- [x] Summary describes intent, not just action +- [x] All parameters documented with constraints +- [x] Return value documented including edge cases +- [x] Exceptions documented with conditions +- [x] Examples provided for common use cases +- [x] Remarks cover gotchas and best practices + +## Before/After +[Include diff showing documentation improvement] +``` + +--- + +### 13.3 AI-Agent Documentation Standards + +Define standards for new code to be AI-agent friendly from the start. + +#### Required Documentation Elements + +```yaml +public_api_requirements: + summary: + - Describe WHAT the method does + - Describe WHEN to use it vs alternatives + - Max 3 sentences + + parameters: + - Type constraints (not just type name) + - Valid value ranges + - Null behavior + - Default values and their implications + + returns: + - Success case + - Empty/null cases + - What properties are populated + + exceptions: + - All thrown exceptions + - Conditions that trigger each + - HTTP status codes for CosmosException + + examples: + - Basic happy path + - Error handling pattern + - Advanced scenario (if complex API) + + remarks: + - Performance implications + - Size/rate limits + - Thread safety + - Retry behavior + + seealso: + - Alternative methods + - Related configuration + - Relevant documentation links +``` + +#### Linting Rules for AI-Friendly Docs + +```yaml +documentation_lint_rules: + - error: "Summary must not be empty for public APIs" + - error: "Parameters must have documentation" + - warning: "Consider adding for complex APIs" + - warning: "Consider adding for methods that throw" + - info: "Consider adding for non-obvious behavior" +``` + +--- + +## 14. Documentation Improvement Tracking + +### 14.1 Issue Label for Docs + +Add label `docs-improvement` to issues that reveal documentation gaps. + +### 14.2 Docs Improvement Backlog Template + +Track documentation improvements separately: + +```markdown +## Documentation Improvement Backlog + +### Microsoft Docs (External) +| Page | Issue | Priority | Status | +|------|-------|----------|--------| +| {docs page} | {gap description} | P0/P1/P2 | Suggested/Filed/Merged | + +### SDK XML Docs (Internal) +| Class/Method | Issue | Priority | Status | +|--------------|-------|----------|--------| +| {class.method} | {gap description} | P0/P1/P2 | Identified/PR/Merged | + +### New Samples Needed +| Scenario | Issue Pattern | Priority | Status | +|----------|--------------|----------|--------| +| {scenario} | #{issue_numbers} | P0/P1/P2 | Identified/Created | +``` + +--- + +## 15. Metrics & Reporting + +Track for continuous improvement: +- Time from issue creation to triage +- Time from triage to investigation complete +- Reproduction success rate +- Workaround acceptance rate +- PR acceptance rate +- False positive rate (wrong root cause) +- Docs improvement suggestions generated +- Docs PRs merged (internal + external) + +--- + +## 16. Lessons Learned (Issue #5547 Case Study) + +This section documents learnings from the first real issue investigation using this plan. + +### 16.1 Issue Details +- **Issue:** #5547 - LINQ Dictionary.Any() generates incorrect SQL +- **Type:** Bug - Query/LINQ +- **Outcome:** Fix implemented, PR created, awaiting CI validation + +### 16.2 What Worked Well + +```yaml +effective_approaches: + parallel_agents: + description: "Using background agents for parallel work" + example: | + - agent-0: Create reproduction test + - agent-1: Implement fix + - agent-2: Run baseline tests + benefit: "Reduced total investigation time significantly" + + local_code_search: + tools_used: + - grep: "Fast pattern matching in source files" + - glob: "Find files by name pattern" + - view: "Read specific file sections" + benefit: "More reliable than remote API when SAML blocks access" + + web_fetch_for_issues: + description: "Use web_fetch when GitHub API is blocked by SAML" + example: "web_fetch https://github.com/Azure/azure-cosmos-dotnet-v3/issues/5547" + benefit: "Can still retrieve issue details without API access" + + stackoverflow_research: + description: "Search StackOverflow for known issues and workarounds" + benefit: "Confirmed issue was known limitation, found workaround pattern" + + incremental_testing: + workflow: + - Run targeted tests first (LINQ-specific) + - Then broader regression tests + - Build verification last + benefit: "Faster feedback loop during development" +``` + +### 16.3 Challenges Encountered + +```yaml +challenges: + github_api_saml: + problem: "Azure org requires SAML authentication for API access" + symptom: "403 error with 'Resource protected by organization SAML enforcement'" + workaround: "Use web_fetch to scrape issue page directly" + recommendation: "Always have web_fetch fallback for GitHub operations" + + github_cli_not_installed: + problem: "gh CLI not available in environment" + symptom: "'gh' is not recognized as a cmdlet" + workaround: "Install with: winget install --id GitHub.cli" + recommendation: "Install gh CLI, authenticate with: gh auth login --web" + + github_cli_authentication: + problem: "gh CLI requires browser authentication for Azure org" + symptom: "SAML SSO required even after gh auth login" + workaround: "Use --web flag and complete browser flow with device code" + recommendation: "gh auth login --web, then authorize for Azure org" + + pr_title_lint_failure: + problem: "PR Lint checks enforce strict title format" + symptom: "PR Lint fails with 'Please follow required format'" + format_required: '"[Internal] Category: (Adds|Fixes|Refactors|Removes) Description"' + examples: + - "LINQ: Fixes Dictionary.Any() to generate correct SQL" + - "Query: Adds support for new aggregate functions" + - "[Internal] Tests: Refactors test infrastructure" + workaround: "gh pr edit {number} --title 'Category: Verb Description'" + recommendation: "Always use correct format from start" + + long_running_tests: + problem: "Full test suite takes several minutes" + symptom: "Commands timeout waiting for completion" + workaround: "Use initial_wait parameter, then read_powershell for polling" + recommendation: "Run targeted tests first, full suite only for final validation" + + ci_gates_long_duration: + problem: "Remote CI gates can take up to 90 minutes" + symptom: "Many checks stay pending for extended periods" + workaround: "Use gh pr checks {number} to monitor, check periodically" + recommendation: "Set up monitoring loop, check every 5-10 minutes" + + agent_completion_time: + problem: "Complex agents (general-purpose with Opus) take 5-10 minutes" + symptom: "read_agent times out multiple times" + workaround: "Use wait:true with longer timeout (180-300s)" + recommendation: "Set expectations for agent completion times" +``` + +### 16.4 Tool Selection Guide (Refined) + +```yaml +tool_selection: + for_issue_retrieval: + first_try: "github-mcp-server-issue_read" + fallback: "web_fetch (when SAML blocks API)" + + for_code_search: + semantic_search: "bluebird do_vector_search" + exact_match: "grep with pattern" + file_discovery: "glob with pattern" + + for_code_reading: + known_path: "view tool directly" + unknown_location: "grep/glob first, then view" + + for_parallel_work: + independent_tasks: "Multiple background agents" + dependent_tasks: "Sequential agent calls" + + for_testing: + quick_validation: "dotnet test --filter (specific tests)" + regression_check: "dotnet test (full suite)" + ci_validation: "Create PR, monitor Azure Pipelines" + + for_pr_creation: + with_gh_cli: "gh pr create --draft" + without_gh_cli: "Provide PR URL and description for manual creation" + + for_ci_monitoring: + check_status: "gh pr checks {pr_number}" + watch_mode: "gh pr checks {pr_number} --watch" + get_failures: "gh pr view {pr_number} --json statusCheckRollup" + fix_title: "gh pr edit {pr_number} --title 'Category: Verb Description'" +``` + +### 16.5 CI Monitoring Workflow (Learned) + +**PR creation triggers 30+ CI checks that can take up to 90 minutes.** + +```yaml +ci_monitoring: + initial_check: + timing: "Immediately after PR creation" + command: "gh pr checks {pr_number}" + expect: "Most checks pending, license/cla may pass quickly" + + common_quick_failures: + pr_lint: + timing: "Within 1 minute" + cause: "PR title doesn't match format" + format: "Category: (Adds|Fixes|Refactors|Removes) Description" + fix: "gh pr edit {number} --title 'LINQ: Fixes Dictionary query translation'" + + license_cla: + timing: "Within 1 minute" + cause: "CLA not signed" + fix: "Sign CLA via link in check details" + + medium_duration_checks: + codeql: + timing: "5-15 minutes" + checks: + - "CodeQL/Analyze (csharp)" + - "CodeQL/Analyze (actions)" + - "CodeQL/Analyze (javascript-typescript)" + - "CodeQL/Analyze (python)" + common_issues: "Security vulnerabilities, code quality" + + static_analysis: + timing: "10-20 minutes" + check: "dotnet-v3-ci (Static Analysis)" + common_issues: "Code style, analyzer warnings" + + long_duration_checks: + unit_tests: + timing: "15-30 minutes" + check: "dotnet-v3-ci (Microsoft.Azure.Cosmos.Tests)" + common_issues: "Test failures, build errors" + + emulator_tests: + timing: "30-60 minutes" + checks: + - "dotnet-v3-ci (EmulatorTests Release - Client Telemetry, Query, ChangeFeed, ReadFeed, Batch)" + - "dotnet-v3-ci (EmulatorTests Release - MultiMaster)" + - "dotnet-v3-ci (EmulatorTests Release - MultiRegion)" + - "dotnet-v3-ci (EmulatorTests Release - Others)" + common_issues: "Integration failures, emulator setup issues" + + full_ci: + timing: "60-90 minutes" + check: "dotnet-v3-ci" + note: "Aggregate check, passes when all sub-checks pass" + + monitoring_loop: + interval: "5-10 minutes" + command: "gh pr checks {pr_number}" + on_failure: + - Get failure details: "gh pr view {number} --json statusCheckRollup" + - Analyze logs if possible + - Fix locally and push + - Wait for re-run + on_all_pass: + - Mark PR ready for review (if draft) + - Notify user +``` + +**CI Check Categories:** + +| Category | Count | Duration | Priority | +|----------|-------|----------|----------| +| Quick (lint, cla) | 2 | < 1 min | Fix immediately | +| CodeQL | 4 | 5-15 min | Usually pass | +| Build/Package | 3 | 10-20 min | Must pass | +| Unit Tests | 3 | 15-30 min | Critical | +| Emulator Tests | 6 | 30-60 min | Critical | +| Preview/Internal | 8 | 20-40 min | Important | +| Encryption | 4 | 20-40 min | If changed | + +### 16.5.1 Azure DevOps Build Re-run via MCP Server + +**Use the Azure DevOps MCP Server to retry failed CI stages.** + +```yaml +mcp_ado_pipelines_update_build_stage: + purpose: "Retry failed stages only (faster than full requeue)" + + tool: "mcp_ado_pipelines_update_build_stage" + parameters: + project: "cosmos-db-sdk-public" + buildId: 59156 + stageRefName: "Preview_Tests_Release" # Stage reference name + state: "retry" # Options: retry, cancel + + example_prompt: | + "Retry the failed 'Preview Tests Release' stage in build 59156 + for project cosmos-db-sdk-public" + + advantages: + - Only re-runs failed stage (faster) + - Doesn't re-run already-passed jobs + - Natural language interface + - Fully automated by Copilot + + requires: + - Azure DevOps MCP Server configured (see Section 0.3) + - Microsoft account with org access +``` + +**Finding the stage reference name:** + +```yaml +# Use MCP server to get build details +mcp_ado_pipelines_get_build_status: + project: "cosmos-db-sdk-public" + buildId: 59156 + +# Response includes timeline with stage names and their refNames +# Common stage refNames in this repo: +# - "Build" +# - "Unit_Tests" +# - "Emulator_Tests_Release" +# - "Preview_Tests_Release" +# - "Static_Analysis" +``` + +**Handling CI Failures:** + +```yaml +ci_failure_response: + flaky_tests: + action: "mcp_ado_pipelines_update_build_stage with state=retry" + + code_related_failures: + action: "Fix code locally, push commit (triggers fresh build)" + + infrastructure_failures: + action: "Wait 5 min, then retry failed stage via MCP" +``` + +### 16.6 Timing Benchmarks + +| Phase | Duration | Notes | +|-------|----------|-------| +| Issue fetch (web_fetch) | ~5s | Faster than API when working | +| Codebase search (grep) | ~2s | Very fast for pattern matching | +| Root cause analysis | ~10min | With Opus model, thorough | +| Reproduction test creation (agent) | ~4min | Background agent | +| Fix implementation (agent) | ~10min | Background agent with Opus | +| Baseline test run | ~2min | Unit tests subset | +| Build verification | ~15s | Incremental build | +| Branch + commit + push | ~30s | Local git operations | + +### 16.7 PR Template Refinements + +Based on this exercise, PRs should include: + +```yaml +pr_requirements: + header: + - "🤖 This PR was authored by GitHub Copilot" + - Links to original issue + + sections: + - Description (what the PR does) + - Root Cause (why the bug existed) + - Changes Made (bullet list) + - Generated SQL/Output (before/after comparison) + - Testing (checklist) + - Checklist (code conventions) + + footer: + - "Generated by GitHub Copilot CLI Agent" + + labels_to_add: + - "copilot-authored" + - Area label (e.g., "Query", "LINQ") +``` + +### 16.8 Recommended Workflow Sequence + +```yaml +recommended_workflow: + phase_1_intake: + duration: "~2 min" + steps: + - Try GitHub API for issue details + - Fallback to web_fetch if SAML blocked + - Classify issue type and area + - Confirm with user before proceeding + + phase_2_research: + duration: "~5 min" + steps: + - Search StackOverflow for known issues + - Search codebase for related code + - Review recent commits/PRs in area + - Document findings + + phase_3_analysis: + duration: "~10 min" + steps: + - Deep code analysis with grep/view + - Use Bluebird for call graph if needed + - Identify root cause + - Document workaround if available + + phase_4_implementation: + duration: "~15 min" + parallel_agents: + - Reproduction test (general-purpose agent) + - Fix implementation (general-purpose agent) + - Baseline tests (task agent) + then: + - Verify all agents completed + - Review changes + - Run regression tests + + phase_5_pr_creation: + duration: "~5 min" + steps: + - Create feature branch (users//copilot--) + - Commit with descriptive message + - Push to remote + - Create draft PR (or provide details for manual creation) + - Monitor CI status +``` + +### 16.9 Flaky Test Registry + +**Known flaky tests that may fail intermittently (not related to code changes):** + +```yaml +flaky_tests: + EndpointFailureMockTest: + location: "Microsoft.Azure.Cosmos.Tests/CosmosClientTests.cs" + symptom: "Intermittent timeout or connection failure" + seen_in: ["PR #5573 (merged)", "PR #5583", "Build 59156"] + action: "Retry failed stage via MCP server" + + RetryTransientIssuesTestAsync: + location: "Microsoft.Azure.Cosmos.EmulatorTests" + symptom: "Transient failure during emulator tests" + seen_in: ["PR #5588", "PR #5587"] + action: "Retry failed stage via ADO API" + + EmulatorTests_Release_Flaky: + location: "CI stage: EmulatorTests Release Flaky" + symptom: "Various timing-related failures" + seen_in: ["PR #5588 (Build 59213)"] + action: "Retry failed stage via ADO API" + +pre_existing_failures: + description: "Tests that consistently fail in master (not flaky)" + note: "These are baseline issues, not caused by your PR changes" + tests: + - name: "TestQueryExecutionInfo_FromString" + location: "OptimisticDirectExecutionQueryBaselineTests.cs" + - name: "TestTextDistributionPlanParsingFromStream" + location: "OptimisticDirectExecutionQueryBaselineTests.cs" + handling: "Ignore if they fail - verify by running on master branch" + +handling_flaky_failures: + step_1: "Check if failed test is in flaky registry" + step_2: "Verify failure is unrelated to your changes (same test fails in other PRs)" + step_3: "Retry failed stage: mcp_ado_pipelines_update_build_stage with state=retry" + step_4: "If still fails after 2 retries, document and proceed (known flaky)" + +adding_to_registry: + when: "Test fails in your PR AND in at least one other recent PR/build" + format: "Add entry with location, symptom, seen_in PRs, recommended action" +``` + +### 16.10 Draft PR Workflow + +**Always create PRs as drafts first, mark ready after CI passes:** + +```yaml +draft_pr_workflow: + create_draft: + command: "gh pr create --draft --title 'Category: Fixes ...' --body-file pr-body.md" + reason: "Prevents premature review requests while CI runs" + + monitor_ci: + command: "gh pr checks {pr_number}" + interval: "Every 5-10 minutes" + duration: "Up to 90 minutes for full CI" + + on_ci_pass: + command: "gh pr ready {pr_number}" + effect: "Removes draft status, notifies reviewers" + + on_ci_fail: + flaky_test: "Retry via MCP server (see Section 16.7)" + code_issue: "Fix locally, push, CI auto-reruns" + + timeline: + - "T+0: Create draft PR" + - "T+1min: Quick checks (lint, CLA) pass/fail" + - "T+15min: CodeQL, static analysis complete" + - "T+30min: Unit tests complete" + - "T+60min: Emulator tests complete" + - "T+90min: All CI complete → mark ready" +``` + +### 16.10.1 CI Gate Monitoring Loop (Goal: ALL GREEN) + +**⚠️ CRITICAL: PR is not complete until ALL CI gates are GREEN.** + +```yaml +ci_monitoring_loop: + goal: "All CI gates GREEN before marking PR ready for review" + + monitoring_workflow: + step_1_initial_check: + wait: "5 minutes after push" + command: "gh pr checks {pr_number}" + check: "Quick gates (lint, CLA) should pass" + on_fail: "Fix immediately (usually PR title format)" + + step_2_periodic_monitoring: + interval: "Every 10 minutes" + command: "gh pr checks {pr_number}" + duration: "Up to 90 minutes" + actions: + - "Note which checks are pending/passing/failing" + - "On first failure, begin investigation immediately" + + step_3_on_failure: + investigate: + - "Get failure logs: gh pr checks {pr_number} --json" + - "Identify failed check name and job ID" + - "Fetch logs via MCP or gh CLI" + - "Analyze root cause" + + categorize_failure: + code_related: "Your changes broke something" + flaky_test: "Known intermittent failure (see Section 16.7)" + infrastructure: "Emulator/network/timeout issues" + unrelated: "Pre-existing failure in master" + + respond: + code_related: + action: "Fix locally → test → push → CI auto-reruns" + verify: "Monitor until that check passes" + + flaky_test: + action: "mcp_ado_pipelines_update_build_stage with state=retry" + max_retries: 2 + if_still_fails: "Document and escalate" + + infrastructure: + action: "Wait 5 min, then retry failed stage via MCP" + + unrelated: + action: "Document that failure exists in master, proceed" + + step_4_iterate: + loop: "Repeat step_2 and step_3 until all gates GREEN" + exit_condition: "gh pr checks shows all checks passing" + + step_5_pre_ready_checks: + description: "Verify branch is ready before marking for review" + checks: + - name: "Ensure branch is up-to-date with master" + commands: | + git fetch origin master + git rebase origin/master + # If conflicts, resolve and push + git push origin {branch_name} --force-with-lease + note: "Rebase ensures clean merge, re-triggers CI if needed" + + - name: "Verify dotnet-v3-ci passed" + command: "gh pr checks {pr_number} --json name,state | jq '.[] | select(.name==\"dotnet-v3-ci\")'" + required: "dotnet-v3-ci must show 'SUCCESS'" + note: "This is the main CI gate - other checks may be informational" + + step_6_complete: + trigger: "ONLY when branch is synced AND dotnet-v3-ci is GREEN" + action: "gh pr ready {pr_number}" + result: "PR moves from draft to ready for review" + note: "Do NOT wait - mark ready as soon as conditions met" + + auto_ready_behavior: + principle: "Mark PR ready for review immediately when conditions met" + required_conditions: + - "Branch is up-to-date with master (rebased)" + - "dotnet-v3-ci check is SUCCESS (main CI gate)" + do_not_wait_for: + - "User confirmation" + - "Additional review" + - "Manual approval" + reason: "CI passing on synced branch = code is validated" + + success_criteria: + required: + - "Branch rebased on latest master" + - "**dotnet-v3-ci check is SUCCESS** (primary gate)" + - "PR lint passed" + - "CLA signed" + optional: + - "CodeQL checks (informational)" + - "Other secondary checks" + + failure_escalation: + after_3_retries: "Investigate deeper, may need human help" + infrastructure_repeated: "Check Azure DevOps service status" + unknown_failure: "Comment on PR with findings, request help" +``` + +### 16.10.2 Post-Ready PR Monitoring & Review Response + +**⚠️ CRITICAL: After marking PR ready, agent MUST monitor for review comments and address them.** + +```yaml +pr_review_monitoring: + principle: "PR is not complete until merged - monitor and respond to all feedback" + + when_to_monitor: + trigger: "After PR is marked ready for review" + frequency: "Every 5-10 minutes while session is active" + until: "PR is merged OR closed" + + monitoring_loop: + interval: "5-10 minutes" + check_ci: "gh pr checks {pr_number}" + check_comments: "gh api repos/{owner}/{repo}/pulls/{pr_number}/comments --jq '.[].created_at' | Select-Object -Last 1" + check_reviews: "gh pr view {pr_number} --json reviews,reviewDecision" + on_new_comment: "Read comment, address feedback, push fix, reply with commit SHA" + on_ci_failure: "Investigate, fix, push" + on_approval: "Merge or wait for additional approvals" + + check_for_reviews: + command: "gh pr view {pr_number} --json reviews,reviewRequests,comments" + also_check: "gh pr view {pr_number} --json reviewDecision" + + review_states: + APPROVED: "PR can be merged (if CI passes)" + CHANGES_REQUESTED: "Must address feedback before merge" + COMMENTED: "Review feedback, may need response" + PENDING: "Awaiting review" + + response_workflow: + step_1_fetch_feedback: + command: | + gh pr view {pr_number} --comments + gh api /repos/{owner}/{repo}/pulls/{pr_number}/reviews + action: "Read all review comments and requested changes" + + step_2_analyze_feedback: + categorize: + - "Code changes required" + - "Questions to answer" + - "Style/formatting suggestions" + - "Test coverage requests" + - "Documentation updates" + + step_3_implement_changes: + action: "Make requested code changes" + follow: "Same process as original implementation" + important: "Address ALL feedback, not just some" + + step_4_full_revalidation: + principle: "Treat as NEW change - full validation required" + required_steps: + - "Local build: dotnet build -c Release" + - "Local unit tests: dotnet test Microsoft.Azure.Cosmos.Tests -c Release" + - "Local emulator tests (if applicable)" + - "Verify fix still works after changes" + + step_5_commit_and_push: + commit_message: "Address review feedback: {summary}" + action: "git push" + + step_6_respond_to_reviewer: + action: "Reply to each review comment explaining resolution" + format: | + > {original comment} + + Addressed in commit {sha}: {explanation} + important: "Do NOT just say 'fixed' - explain what was changed" + + step_7_request_re_review: + command: "gh pr edit {pr_number} --add-reviewer {reviewer}" + or: "Comment @{reviewer} - feedback addressed, please re-review" + + step_8_monitor_ci: + action: "Wait for CI to complete on new commits" + same_as: "Original CI monitoring workflow" + + review_response_patterns: + code_change_requested: + bad: "Fixed." + good: | + Addressed in abc1234: + - Changed `foo()` to use `bar()` as suggested + - Added null check per review feedback + - Updated test to cover edge case + + question_asked: + bad: "Yes" or "No" + good: | + Good question! The reason for this approach is: + 1. {explanation} + 2. {trade-off considered} + Alternative considered: {alternative and why not chosen} + + style_suggestion: + action: "Apply if reasonable, explain if declining" + declining_format: | + Considered this suggestion. Keeping current approach because: + - {reason} + If you feel strongly, happy to change. + + iteration_loop: + principle: "Repeat until APPROVED or merged" + + loop: + - "Check for new reviews/comments" + - "Address all feedback" + - "Full revalidation (build, test)" + - "Push changes" + - "Respond to reviewers" + - "Wait for CI" + - "Check review status" + - "If CHANGES_REQUESTED → repeat loop" + - "If APPROVED → ready for merge" + + evidence_required: + per_iteration: + - "Review comments addressed: {list}" + - "Local build: exit code 0" + - "Local tests: X/Y passed" + - "CI status: {status}" + - "Reviewer notified: {comment_url}" +``` + +**Quick Reference Commands:** + +```bash +# Check PR reviews and status +gh pr view {pr_number} --json reviews,reviewDecision,state + +# Get review comments +gh pr view {pr_number} --comments + +# Get detailed review threads +gh api /repos/Azure/azure-cosmos-dotnet-v3/pulls/{pr_number}/reviews + +# Request re-review after addressing feedback +gh pr edit {pr_number} --add-reviewer {username} + +# Check if PR is mergeable +gh pr view {pr_number} --json mergeable,mergeStateStatus +``` + +```bash +# Check all PR gates +gh pr checks {pr_number} + +# Get detailed status as JSON +gh pr checks {pr_number} --json name,state,conclusion + +# View specific check logs (GitHub Actions) +gh run view {run_id} --log-failed + +# Retry failed stage (Azure DevOps via MCP) +# Use: mcp_ado_pipelines_update_build_stage +# project: "cosmos-db-sdk-public" +# buildId: {build_id} +# stageRefName: "{failed_stage}" +# state: "retry" +``` + +### 16.11 Parallel Agent Strategy + +**Use parallel background agents to maximize efficiency:** + +```yaml +parallel_agent_pattern: + when: "Multiple independent tasks can run simultaneously" + + example_investigation: + launch_simultaneously: + agent_1: + type: "general-purpose" + task: "Create reproduction test" + model: "claude-opus-4.5" + + agent_2: + type: "general-purpose" + task: "Implement fix based on root cause" + model: "claude-opus-4.5" + + agent_3: + type: "task" + task: "Run baseline tests to establish pass/fail state" + model: "claude-haiku-4.5" + + then: + - "Wait for all agents to complete" + - "Review each agent's output" + - "Integrate changes if all successful" + - "Run regression tests" + + benefits: + - "3 agents finish in ~10min vs ~30min sequential" + - "Each agent has full context window" + - "Failures isolated to specific task" + + caution: + - "Don't parallelize dependent tasks" + - "Review all outputs before committing" + - "One agent's fix may conflict with another's" +``` + +### 16.11.1 Parallel Issue Handling (While CI Waits) + +**While waiting for CI on one issue, start investigation on the next issue.** + +```yaml +parallel_issue_workflow: + principle: "Don't wait idle - use CI wait time productively" + + workflow: + time_0: + issue_A: "Create PR, CI starts (60-90 min wait)" + action: "Start investigation on Issue B" + + time_30min: + issue_A: "CI running (~50% complete)" + issue_B: "Root cause identified, fix implemented" + action: "Run local tests for Issue B" + + time_60min: + issue_A: "CI running (~80% complete)" + issue_B: "Local tests pass, create PR, CI starts" + action: "Start investigation on Issue C (if available)" + + time_90min: + issue_A: "CI complete → mark ready for review" + issue_B: "CI running" + issue_C: "Investigation in progress" + + state_tracking: + per_issue: + - "Branch name: users//copilot--" + - "Investigation doc: ~/.copilot/session-state/.../files/issue-{n}-investigation.md" + - "PR number (once created)" + - "CI status" + + session_state: + recommended: "Track in session plan.md" + format: | + ## Active Issues + | Issue | Branch | Status | PR | CI | + |-------|--------|--------|----|----| + | #5547 | copilot-5547-linq-dict | Ready for review | #5583 | ✅ | + | #5550 | copilot-5550-retry | CI running | #5590 | ⏳ | + | #5555 | (none) | Investigating | - | - | + + commands: + switch_to_issue: + - "git stash (if uncommitted work)" + - "git checkout users//copilot--" + - "Continue work on that issue" + + check_all_ci: + command: "gh pr list --author @me --json number,title,statusCheckRollup" + + limits: + recommended_parallel: 2-3 + reason: "Context quality degrades with too many active issues" + + best_practices: + - "Complete one issue to PR-created before starting next" + - "Don't start new issue if current one needs active debugging" + - "Check CI status periodically for all active PRs" + - "Mark PRs ready as soon as CI passes (don't forget!)" +``` + +**Quick Commands for Multi-Issue Workflow:** + +```powershell +# Check status of all your PRs +gh pr list --author @me --repo Azure/azure-cosmos-dotnet-v3 + +# Check CI for specific PR +gh pr checks {pr_number} + +# Switch to different issue branch +git stash +git checkout users/{name}/copilot-{other-issue}-{feature} + +# List all your branches +git branch --list "users/*" +``` + +--- + +## 17. Lessons Learned (Performance/Memory Issues) + +This section documents generalizable learnings from performance and memory-related investigations. + +### 17.1 Key Learnings: Performance/Benchmark PRs + +**⚠️ CRITICAL: For any performance or benchmark-related PR, measurements are REQUIRED.** + +```yaml +performance_pr_requirements: + measurements: + required: true + must_include: + - "Environment details (machine, .NET version, config)" + - "Before measurement (baseline)" + - "After measurement (with fix)" + - "Improvement percentage" + - "Explanation of why measurement proves the fix" + + pr_description_template: + section: "## Benchmark Results" + table_format: | + | Metric | Before | After | Improvement | + |--------|--------|-------|-------------| + | {metric} | {before} | {after} | {improvement} | + + must_explain: + - "Why this measurement validates the fix" + - "What would happen without the fix (consequence)" + - "How measurement methodology is reliable" +``` + +### 17.2 Pre-existing Test Failures + +**Document pre-existing failures to distinguish from regression:** + +```yaml +pre_existing_failures: + purpose: "Distinguish regressions from known issues" + + approach: + step_1: "Run tests on clean master before making changes" + step_2: "Document any failures as 'pre-existing'" + step_3: "After fix, compare failure list - new failures = regression" + + documentation_format: + in_pr_description: | + *Pre-existing failures, not related to this change + + ci_implications: + - "Pre-existing failures may cause CI to fail" + - "Check if same tests fail in recent passing PRs" + - "If test passes in master but fails in PR, it's a regression" +``` + +### 17.3 Check for Existing PRs First + +**⚠️ CRITICAL: Always check if an existing PR addresses the issue before starting work.** + +```yaml +existing_pr_check: + when: "Before starting any investigation beyond triage" + + how_to_check: + command: "gh pr list --repo Azure/azure-cosmos-dotnet-v3 --search 'is:open {issue_keywords}'" + or: "Check issue page for 'linked pull requests' section" + + if_pr_exists: + action: "Review existing PR instead of creating duplicate" + options: + - "Add benchmark/validation tests to existing PR" + - "Review and provide feedback" + - "Offer to help resolve CI failures" + - "Create complementary PR (e.g., benchmark only) if appropriate" +``` + +### 17.4 CI Gate Not Started Pattern + +**When CI gates show "pending" but never start:** + +```yaml +ci_not_started_pattern: + symptoms: + - "PR created but dotnet-v3-ci shows 'Expected — Waiting for status to be reported'" + - "No CI jobs running after 30+ minutes" + - "Other PRs in same repo have CI running" + + possible_causes: + author_permission: + description: "Author doesn't have write access to trigger CI" + fix: "Maintainer needs to approve workflow run" + check: "Look for 'Approve and run' button on PR Actions tab" + + ci_configuration: + description: "CI not configured to run on this branch pattern" + fix: "Check azure-pipelines.yml trigger configuration" + + branch_policy: + description: "Branch policies require specific conditions" + fix: "Check repository branch protection rules" + + quota_exhaustion: + description: "Org has exhausted CI minutes" + fix: "Wait or contact org admins" + + investigation_steps: + - "Check PR 'Checks' tab for any status" + - "Check if author is external contributor (first-time approval needed)" + - "Compare with recent PRs from same author" + - "Check Azure Pipelines dashboard for queue status" +``` + +### 17.5 Test Filter Patterns from CI + +**Reference for running CI-equivalent tests locally:** + +```yaml +ci_test_filters: + source: "templates/build-test.yml" + + emulator_pipeline_1: + categories: "Query, ReadFeed, Batch, ChangeFeed" + filter: '--filter "TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed"' + excludes: "Flaky, Quarantine, LongRunning, MultiRegion, MultiMaster" + + emulator_pipeline_2: + categories: "Others (everything not in Pipeline 1)" + filter: '--filter "TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed"' + excludes: "Flaky, Quarantine, LongRunning, MultiRegion, MultiMaster" + + local_safe_categories: + can_run: ["Query", "ReadFeed", "Batch", "ChangeFeed", "LINQ"] + requires_emulator: true + + ci_only_categories: + multiregion: "Requires Azure secrets, multi-region setup" + multimaster: "Requires Azure secrets, multi-master setup" + + common_excludes: + always: '--filter "TestCategory!=Quarantine & TestCategory!=Ignore"' + reason: "Quarantined tests are known failures, Ignore tests are skipped" +``` + +### 17.6 Benchmark Test Pattern + +**Template for creating benchmark/validation tests:** + +```csharp +// File: {Area}/{FeatureName}Benchmark.cs +[TestClass] +public class {FeatureName}BenchmarkTests +{ + /// + /// Compares performance between old and new approach. + /// This test validates that the fix reduces overhead. + /// + [TestMethod] + [TestCategory("LINQ")] // Appropriate category + public void {MethodName}_PerformanceImpact() + { + // Arrange + const int iterations = 1000; + var testData = CreateTestData(); + + // Act - Baseline (old approach) + var swBaseline = Stopwatch.StartNew(); + for (int i = 0; i < iterations; i++) + { + OldApproach(testData); + } + swBaseline.Stop(); + + // Act - New approach + var swNew = Stopwatch.StartNew(); + for (int i = 0; i < iterations; i++) + { + NewApproach(testData); + } + swNew.Stop(); + + // Assert - New should be faster + Trace.WriteLine($"Baseline: {swBaseline.ElapsedMilliseconds}ms"); + Trace.WriteLine($"New: {swNew.ElapsedMilliseconds}ms"); + Trace.WriteLine($"Speedup: {(double)swBaseline.ElapsedMilliseconds / swNew.ElapsedMilliseconds:F1}x"); + + // Validate improvement (adjust threshold as needed) + Assert.IsTrue(swNew.ElapsedMilliseconds < swBaseline.ElapsedMilliseconds, + $"New approach should be faster. Baseline={swBaseline.ElapsedMilliseconds}ms, New={swNew.ElapsedMilliseconds}ms"); + } +} +``` + +### 17.7 Workflow Summary for Performance Issues + +```yaml +recommended_workflow_for_perf_issues: + phase_1_triage: + - "Check for existing PRs first" + - "Classify as performance/memory issue" + - "Identify measurement approach" + + phase_2_analysis: + - "Locate root cause code" + - "Understand memory/performance impact" + - "Research fix options (.NET docs, patterns)" + + phase_3_validation: + - "Create benchmark test" + - "Run baseline measurement" + - "Apply fix or validate existing PR's approach" + - "Run post-fix measurement" + - "Document improvement (required for PR)" + + phase_4_pr: + - "Include benchmark results table in PR description" + - "Explain why measurements prove the fix" + - "Note any pre-existing test failures" + - "Link to related issues/PRs" + + phase_5_learnings: + - "Capture generalizable patterns discovered" + - "Update agent plan with reusable workflows" + - "Exclude issue-specific details" +``` + +### 17.8 Session Learnings Capture (Final Step) + +**⚠️ REQUIRED: At the end of each investigation session, capture learnings.** + +```yaml +session_learnings: + when: "End of investigation session, before closing" + purpose: "Continuously improve the agent plan with real-world patterns" + + what_to_capture: + include: + - "New workflow patterns that worked well" + - "Tool selection improvements" + - "Error handling patterns" + - "CI/testing shortcuts discovered" + - "Documentation gaps filled" + - "Troubleshooting steps for common issues" + + exclude: + - "Issue-specific technical details (e.g., specific bug root causes)" + - "One-off code patterns unlikely to recur" + - "Temporary workarounds" + - "Measurements/benchmarks from specific issues" + + how_to_update: + step_1: "Review session for generalizable patterns" + step_2: "Draft additions to relevant section of agent plan" + step_3: "Remove any issue-specific examples" + step_4: "Commit with message: 'Docs: Add learnings from session'" + + checklist: + - "Is this pattern reusable for other issues?" + - "Would a future agent benefit from knowing this?" + - "Is it free of issue-specific details?" + - "Does it fit an existing section or need a new one?" + + examples_of_good_learnings: + - "Performance PRs require benchmark measurements in description" + - "Check for existing PRs before starting investigation" + - "CI gates may not start for external contributors" + - "Pre-existing test failures should be documented" + + examples_of_bad_learnings: + - "Issue #5487 was caused by Expression.Compile()" # Too specific + - "Fixed by using preferInterpretation: true" # Issue-specific fix + - "Build 59156 had a flaky test" # One-off occurrence +``` + +**Quick Prompt for End of Session:** + +``` +Review this session and identify any generalizable learnings +(workflow patterns, tool usage, troubleshooting steps) that should +be added to .github/copilot-agent-plan.md. Exclude issue-specific +technical details. +``` + +--- + +## 18. Reference Templates & Patterns + +### 18.1 Investigation Document Template + +**Create investigation docs in session workspace for complex issues:** + +```yaml +investigation_document: + location: "~/.copilot/session-state/{session-id}/files/issue-{number}-investigation.md" + purpose: "Persist findings across context compactions" + + template: | + # Issue #{number} Investigation + + ## Summary + - **Issue**: [one-line description] + - **Reporter**: [username] + - **Area**: [LINQ/Query/SDK/etc] + - **Status**: [Triaging/Investigating/Reproducing/Fixing/Complete] + + ## Reported Behavior + [Copy from issue description] + + ## Expected Behavior + [What should happen] + + ## Root Cause Analysis + - **Location**: [file:line] + - **Cause**: [why the bug exists] + - **Evidence**: [code snippets, logs] + + ## Reproduction + - **Test file**: [path to test] + - **Steps**: [how to reproduce] + - **Result**: [confirmed/not-reproduced] + + ## Fix Strategy + - **Approach**: [how to fix] + - **Files to modify**: [list] + - **Risk assessment**: [low/medium/high] + + ## Verification + - [ ] Unit tests pass + - [ ] Existing tests still pass + - [ ] Build succeeds + - [ ] CI passes + + ## References + - [Links to related issues, docs, PRs] + + when_to_create: + - "Complex issues requiring multi-step analysis" + - "Issues that may span multiple sessions" + - "When context compaction is likely" +``` + +### 18.2 Commit Message Format + +**Follow conventional commit format for this repository:** + +```yaml +commit_format: + pattern: "{type}: {description}" + + types: + - "Fix" - Bug fix + - "Add" - New feature + - "Refactor" - Code restructuring + - "Test" - Adding tests + - "Docs" - Documentation + - "Perf" - Performance improvement + + examples: + bug_fix: "Fix: Dictionary.Any() now uses OBJECTTOARRAY instead of JOIN" + new_feature: "Add: Support for hierarchical partition keys" + test: "Test: Add unit tests for IsDictionary type detection" + docs: "Docs: Update LINQ query translation documentation" + + rules: + - "Keep first line under 72 characters" + - "Use imperative mood ('Fix' not 'Fixed')" + - "Reference issue number in body if applicable" + - "Sign-off required for external contributors" + + copilot_attribution: + requirement: "All Copilot-assisted commits MUST include co-author trailer" + format: | + Co-authored-by: Copilot Autofix powered by GitHub Advanced Security + example: | + Fix: Dictionary.Any() now uses OBJECTTOARRAY instead of JOIN + + The LINQ translator was treating Dictionary as a generic + IEnumerable, generating incorrect SQL with JOIN. + + Fixes #5547 + + Co-authored-by: Copilot Autofix powered by GitHub Advanced Security + + full_example: | + Fix: Dictionary.Any() now uses OBJECTTOARRAY instead of JOIN + + The LINQ translator was treating Dictionary as a generic + IEnumerable, generating incorrect SQL with JOIN. Now wraps + dictionary member access with OBJECTTOARRAY() function. + + Fixes #5547 +``` + +### 18.3 PR Description Template + +**Full investigation details for Copilot-authored PRs:** + +```yaml +pr_description_template: | + ## Description + 🤖 **This PR was authored by GitHub Copilot** + + Fixes #{issue_number} + + [One paragraph explaining what the PR does] + + ## Root Cause + + **Location**: `{file_path}:{line_number}` + + **Analysis**: + [Detailed explanation of why the bug existed] + + **Evidence**: + ```csharp + // Code snippet showing the problematic behavior + ``` + + ## Changes Made + + ### {file_name_1} + - [Change description] + + ### {file_name_2} + - [Change description] + + ## Generated Output + + **Before (incorrect)**: + ```sql + [incorrect output] + ``` + + **After (correct)**: + ```sql + [correct output] + ``` + + ## Testing + + - [ ] New unit tests added: `{test_file_name}` + - [ ] Existing tests pass + - [ ] Local build succeeds + - [ ] Tested with Cosmos DB emulator (if applicable) + + ## Checklist + + - [x] Code follows repository conventions + - [x] Changes are minimal and focused + - [x] No unrelated changes included + - [x] Documentation updated (if applicable) + + ## Plan Compliance Audit + + | Checkpoint | Status | Notes | + |------------|--------|-------| + | Searched existing PRs before branch | ✅/❌ | `gh pr list --search '{issue}'` | + | Searched existing branches | ✅/❌ | `git branch -a \| grep {issue}` | + | Local build succeeded | ✅/❌ | Exit code 0, 0 errors | + | Full unit tests passed | ✅/❌ | Or note pre-existing failures | + | Emulator tests passed | ✅/❌ | Or note if not applicable | + | Root cause documented | ✅/❌ | Code path traced | + | Investigation steps included | ✅/❌ | Summary in PR body | + | Issue comment posted | ✅/❌ | Link to comment | + + --- + *Generated by GitHub Copilot CLI Agent* + +pr_title_format: + pattern: "Category: (Adds|Fixes|Refactors|Removes) Description" + lint_regex: "^\\[Internal\\]\\s.+:\\s(Adds|Fixes|Refactors|Removes)\\s.+" + examples: + - "LINQ: Fixes Dictionary.Any() to generate correct SQL with OBJECTTOARRAY" + - "SDK: Adds support for hierarchical partition keys" + - "Query: Refactors SQL generation for better readability" +``` + +### 18.4 Code Style (StyleCop & EditorConfig) + +**Repository uses StyleCop.Analyzers and .editorconfig for code style enforcement.** + +```yaml +stylecop: + config_file: "Microsoft.Azure.Cosmos/src/stylecop.json" + package: "StyleCop.Analyzers v1.1.118" + + key_rules: + documentation: + company_name: "Microsoft" + document_internal: false + xml_header: false + ordering: + system_usings_first: true + readability: + no_builtin_type_aliases: true # Use 'string' not 'String' + +editorconfig: + file: ".editorconfig" + + critical_rules: + indentation: "4 spaces (no tabs)" + line_endings: "CRLF" + final_newline: false + + this_qualification: "required (error level)" + # this.field, this.Property, this.Method(), this.Event + + var_usage: "never (error level)" + # Always use explicit types: string x = ""; not var x = ""; + + usings_placement: "inside namespace (error level)" + + braces: "Allman style (open brace on new line)" + + common_violations: + - "Missing this. qualifier" + - "Using var instead of explicit type" + - "Usings outside namespace" + - "Missing parentheses in binary operators" +``` + +**Quick Style Checklist:** +- [ ] `this.` prefix on all instance members +- [ ] Explicit types (no `var`) +- [ ] `using` statements inside namespace +- [ ] System usings first +- [ ] 4-space indentation +- [ ] CRLF line endings + +### 18.5 Async/Await & CancellationToken Patterns + +```yaml +async_patterns: + all_public_async_methods: + - "Must accept CancellationToken as last parameter" + - "Must pass CancellationToken to all async calls" + - "Must use ConfigureAwait(false) for library code" + + naming: + - "Async suffix required: ReadItemAsync, CreateContainerAsync" + + example: | + public async Task> ReadItemAsync( + string id, + PartitionKey partitionKey, + ItemRequestOptions requestOptions = null, + CancellationToken cancellationToken = default) + { + return await this.container.ReadItemAsync( + id, + partitionKey, + requestOptions, + cancellationToken).ConfigureAwait(false); + } + + anti_patterns: + - "Never use .Result or .Wait() - causes deadlocks" + - "Never ignore CancellationToken parameter" + - "Never create fire-and-forget tasks without error handling" +``` + +### 18.6 Error Handling Patterns + +```yaml +exception_handling: + primary_exception: "CosmosException" + location: "Microsoft.Azure.Cosmos.Resource.CosmosExceptions" + + factory_usage: + - "Use CosmosExceptionFactory.Create() for new exceptions" + - "Use CosmosExceptionFactory.CreateNotFoundException()" + - "Use CosmosExceptionFactory.CreateBadRequestException()" + + catching_pattern: | + try + { + ResponseMessage response = await this.SendAsync(...); + } + catch (CosmosException cosmosException) when (cosmosException.StatusCode == HttpStatusCode.NotFound) + { + // Handle not found + } + catch (CosmosException cosmosException) + { + DefaultTrace.TraceError($"Operation failed: {cosmosException.StatusCode}"); + throw; + } + + status_code_handling: + "400 BadRequest": "Invalid input, don't retry" + "404 NotFound": "Resource doesn't exist" + "409 Conflict": "Concurrency conflict, may retry with new etag" + "429 TooManyRequests": "Throttled, retry after RetryAfter" + "503 ServiceUnavailable": "Transient, retry with backoff" +``` + +### 18.7 Logging Conventions + +```yaml +logging: + class: "DefaultTrace" + namespace: "Microsoft.Azure.Cosmos.Tracing" + + methods: + info: "DefaultTrace.TraceInformation()" + warning: "DefaultTrace.TraceWarning()" + error: "DefaultTrace.TraceError()" + critical: "DefaultTrace.TraceCritical()" + verbose: "DefaultTrace.TraceVerbose()" + + format: + - "Include context: operation, resource, status" + - "Use string interpolation with $" + - "Include relevant IDs for debugging" + + example: | + DefaultTrace.TraceInformation( + $"ReadItem completed. Container: {this.containerId}, " + + $"Item: {itemId}, StatusCode: {response.StatusCode}"); + + when_to_log: + always: "Errors, retries, throttling" + verbose: "Successful operations, timing" + never: "Sensitive data, PII, keys" +``` + +### 18.8 Breaking Change Detection + +```yaml +api_contracts: + contract_files: + - "Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.net6.json" + - "Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKTelemetryAPI.net6.json" + + update_script: "UpdateContracts.ps1" + + check_command: | + dotnet test --filter "TestCategory=UpdateContract" --configuration Release + + breaking_changes: + prohibited: + - "Removing public API" + - "Changing method signatures" + - "Changing return types" + - "Removing properties" + - "**Changing project TargetFramework**" + allowed_with_review: + - "Adding new optional parameters" + - "Adding new methods/properties" + - "Deprecating (not removing) APIs" + + project_constraints: + target_frameworks: + rule: "NEVER change TargetFramework in .csproj files" + reason: "SDK targets netstandard2.0 for broad compatibility" + current_targets: + sdk: "netstandard2.0" + tests: "net6.0" + if_needed: "Discuss with team before any framework changes" + + before_pr: + - "Run UpdateContracts.ps1" + - "Review contract diff" + - "If breaking, document in PR and get explicit approval" +``` + +### 18.9 Security Review Checklist + +```yaml +security_review: + required_for: + - "Changes to Authorization/" + - "Changes to encryption code" + - "Changes to credential handling" + - "New external HTTP calls" + + checklist: + - "[ ] No secrets/keys logged or exposed" + - "[ ] Credentials not stored in plain text" + - "[ ] Input validation on user-provided data" + - "[ ] No SQL injection in generated queries" + - "[ ] Proper certificate validation" + - "[ ] CancellationToken honored (no indefinite waits)" + + encryption_specific: + - "[ ] Keys properly scoped" + - "[ ] Encryption algorithms approved" + - "[ ] Key rotation supported" +``` + +### 18.10 Performance Considerations + +```yaml +performance: + hot_path_rules: + - "Minimize allocations in request path" + - "Use Span/Memory for buffer operations" + - "Avoid LINQ in hot paths (use foreach)" + - "Pool objects where possible (ArrayPool)" + + benchmarking: + location: "Microsoft.Azure.Cosmos.Samples/Tools/Benchmark" + url: "https://github.com/Azure/azure-cosmos-dotnet-v3/tree/master/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark" + important: "ALL benchmarks go here ONLY - do not add benchmarks elsewhere" + + adding_benchmarks: + step_1: "Add benchmark class to Benchmark project" + step_2: "Follow existing patterns in the project" + step_3: "Run locally to validate" + + running_benchmarks: + cd: "Microsoft.Azure.Cosmos.Samples/Tools/Benchmark" + help: "dotnet run -c Release -- --help" + example: "dotnet run -c Release -- -e {endpoint} -k {key}" + + common_issues: + boxing: "Avoid boxing value types" + closures: "Be careful with lambda captures" + strings: "Use StringBuilder for concatenation" + + before_pr: + - "Check if change is in hot path" + - "Run relevant benchmarks if performance-sensitive" + - "Document any expected performance impact" + - "Add new benchmarks to Benchmark project if needed" +``` + +### 18.11 Rollback Strategy + +```yaml +rollback: + if_pr_causes_issues: + immediate: + - "Revert PR: gh pr revert {number}" + - "Or: git revert {commit} && git push" + + investigation: + - "Create issue documenting the problem" + - "Link to reverted PR" + - "Analyze what was missed" + + prevention: + - "Draft PR with CI validation" + - "Require review before merge" + - "Monitor after merge for 24h" +``` + +### 18.12 Testing Patterns + +```yaml +testing: + unit_tests: + location: "Microsoft.Azure.Cosmos.Tests" + pattern: "{ClassName}Tests.cs" + framework: "MSTest" + + emulator_tests: + location: "Microsoft.Azure.Cosmos.EmulatorTests" + requires: "Cosmos DB Emulator running" + category: "[TestCategory(\"Emulator\")]" + + mocking: + - "Mock external dependencies (HTTP, network)" + - "Use ResponseMessage for response mocking" + - "Don't mock internal implementation details" + + test_naming: | + [TestMethod] + public async Task MethodName_Scenario_ExpectedResult() + { + // Arrange + // Act + // Assert + } + + baseline_tests: + location: "Microsoft.Azure.Cosmos.Tests/BaselineTest" + purpose: "Capture expected output for comparison" + update: "Run UpdateContracts.ps1 to refresh baselines" +``` + +### 18.13 Dynamic .NET Version Testing + +**For issues that only reproduce on newer .NET versions (e.g., .NET 10):** + +```yaml +dynamic_version_testing: + purpose: "Test behavior in .NET versions not in main test matrix" + use_when: + - "Issue only reproduces on .NET 10+" + - "Compiler behavior changed in newer .NET" + - "Need to verify fix works across versions" + + approach: + description: "Dynamically generate, compile, and run test project" + steps: + 1: "Create temp directory" + 2: "Generate .csproj targeting specific .NET version" + 3: "Generate test .cs file with repro code" + 4: "Run dotnet build && dotnet run" + 5: "Parse output for pass/fail" + 6: "Clean up temp directory" + + implementation: + test_method: | + [TestMethod] + public void TestNet10Behavior() + { + string tempDir = Path.Combine(Path.GetTempPath(), $"cosmos-test-{Guid.NewGuid()}"); + try + { + Directory.CreateDirectory(tempDir); + + // Generate .csproj + string csproj = @" + + + Exe + net10.0 + + + + + "; + File.WriteAllText(Path.Combine(tempDir, "Test.csproj"), csproj); + + // Generate test code + string testCode = @" + using Microsoft.Azure.Cosmos; + // Repro code from issue + var array = new[] { ""a"" }; + var query = container.GetItemLinqQueryable() + .Where(x => array.Contains(x.Name)); + Console.WriteLine(query.ToString()); + "; + File.WriteAllText(Path.Combine(tempDir, "Program.cs"), testCode); + + // Build and run + var result = RunProcess("dotnet", "run", tempDir); + + // Assert + Assert.IsFalse(result.Contains("NotSupportedException")); + } + finally + { + Directory.Delete(tempDir, recursive: true); + } + } + + benefits: + - "Tests actual compiler behavior for target .NET version" + - "No permanent test project in repository" + - "Can test any .NET version installed on machine" + - "Catches version-specific regressions" + + prerequisites: + - "Target .NET SDK must be installed (e.g., .NET 10 SDK)" + - "Check with: dotnet --list-sdks" + + when_not_to_use: + - "Issue reproduces on existing test framework version" + - "CI doesn't have required SDK installed" + - "Simple unit test is sufficient" +``` + +### 18.14 Network and Region Debugging Techniques + +**Useful techniques for investigating connectivity and region-related issues:** + +```yaml +network_debugging: + dns_verification: + description: "Verify Cosmos DB endpoint DNS resolution" + commands: + - "nslookup .documents.azure.com 8.8.8.8" + - "nslookup -.documents.azure.com 8.8.8.8" + example: | + # Main endpoint + nslookup hkms-local-default-store.documents.azure.com 8.8.8.8 + + # Regional endpoint + nslookup hkms-local-default-store-eastus.documents.azure.com 8.8.8.8 + + interpret_results: + - "NXDOMAIN = endpoint doesn't exist in DNS" + - "CNAME chain shows actual backend server" + - "Compare main vs regional to verify account exists" + + region_proximity_util: + description: "Inspect SDK's preferred region list (internal)" + location: "Microsoft.Azure.Cosmos.Direct assembly" + method: "RegionProximityUtil.GeneratePreferredRegionList(region)" + test_code: | + // Use reflection to call internal method + var cosmosDir = Path.GetDirectoryName(typeof(CosmosClient).Assembly.Location); + var directAsm = Assembly.LoadFrom(Path.Combine(cosmosDir, "Microsoft.Azure.Cosmos.Direct.dll")); + var utilType = directAsm.GetTypes().First(x => x.Name == "RegionProximityUtil"); + var method = utilType.GetMethod("GeneratePreferredRegionList", BindingFlags.Public | BindingFlags.Static); + var result = method.Invoke(null, new object[] { "East US" }) as List; + + notes: + - "Preferred list is from static proximity table" + - "Only used when main endpoint is unavailable" + - "SDK will iterate through list on failures" + + common_issues: + nsp_blocked: + symptom: "Main endpoint unreachable, falls back to regional" + cause: "Network Security Perimeter (NSP) enabled on account" + diagnosis: "App running outside NSP boundary" + resolution: "Add app network/IP to NSP allowed list" + + staging_in_list: + symptom: "SDK tries *stg endpoints (e.g., eastusstg)" + cause: "Staging regions in proximity table" + note: "This is data issue in Microsoft.Azure.Cosmos.Direct" + + imds_errors: + symptom: "Socket error to 169.254.169.254:80" + meaning: "Azure IMDS (Instance Metadata Service) unreachable" + note: "Often unrelated noise - not root cause" + context: "Common in non-Azure or restricted environments" + + endpoint_construction: + file: "Microsoft.Azure.Cosmos/src/Routing/LocationHelper.cs" + method: "GetLocationEndpoint(serviceEndpoint, location)" + logic: | + // "East US" -> "eastus" (spaces removed) + // contoso.documents.azure.com + "East US" + // -> contoso-eastus.documents.azure.com + note: "Only called when main endpoint unavailable" +``` + +--- + +### 18.15 GitHub Comment Attribution + +**All GitHub comments posted by Copilot agents must include attribution:** + +```yaml +github_comment_attribution: + required: true + format: | + --- + *Co-authored with GitHub Copilot* 🤖 + + placement: "End of comment" + + example: | + Hi @user, + + Thank you for reporting this issue. We investigated and found... + + [Main content here] + + --- + *Co-authored with GitHub Copilot* 🤖 + + rationale: + - "Transparency about AI-assisted responses" + - "Helps reviewers understand context" + - "Builds trust with community" +``` + +### 18.16 Awaiting Customer Response Workflow + +**When investigation requires customer input before proceeding:** + +```yaml +awaiting_customer_workflow: + trigger: + - "Need reproduction steps or environment details" + - "Question about expected behavior vs actual behavior" + - "Need to confirm hypothesis (e.g., NSP enabled, firewall rules)" + - "Missing information to reproduce issue" + + steps: + 1_post_comment: + action: "Post detailed comment with specific questions" + include: + - "Summary of investigation so far" + - "Specific question(s) - numbered for easy response" + - "Why this information is needed" + - "Attribution footer" + + 2_add_label: + action: "gh issue edit {issue_number} --add-label 'waiting-for-author'" + purpose: "Visible status for triage" + + 3_track_status: + action: "Update session plan with awaiting status" + format: "| #{issue} | Description | 💬 Awaiting customer | - | - |" + + 4_set_reminder: + note: "Check back in 3-5 business days" + followup_if_no_response: | + - Ping author with gentle reminder + - After 14 days: Consider closing with "needs-info" label + + example_comment: | + ## Investigation Update + + We've analyzed the issue and found the following: + + **Findings:** + - Main endpoint `account.documents.azure.com` resolves correctly + - Regional endpoint `account-eastusstg.documents.azure.com` returns NXDOMAIN + - SDK's RegionProximityUtil returns "East US STG" as first fallback region + + **Question:** + 1. Is Network Security Perimeter (NSP) enabled on this Cosmos DB account? + 2. Is the application running inside a VNet or behind a firewall? + + This will help us confirm whether the DNS failure is expected (NSP blocks + direct access) or indicates a configuration issue. + + --- + *Co-authored with GitHub Copilot* 🤖 + + parallel_work: + while_waiting: + - "Continue with other issues in queue" + - "Don't block on customer response" + - "Track multiple awaiting issues in session plan" +``` + +### 18.17 Issue Status Tracking in Session + +**Maintain a status table when working multiple issues:** + +```yaml +session_status_table: + format: | + | Issue | Description | Status | PR | CI | + |-------|-------------|--------|----|----| + | #5547 | LINQ Dictionary | ✅ Ready for review | #5583 | ✅ 35/35 | + | #5518 | LINQ ReadOnlySpan | ✅ Ready for review | #5585 | ✅ 34/34 | + | #5578 | DNS eastusstg | 💬 Awaiting customer | - | - | + + status_icons: + investigating: "🔍" + coding: "💻" + ci_running: "🔄" + ci_passed: "✅" + ci_failed: "❌" + awaiting_customer: "💬" + ready_for_review: "✅" + merged: "🎉" + blocked: "🚫" + + update_triggers: + - "After posting PR" + - "After CI completes" + - "After marking ready for review" + - "After posting comment awaiting response" + - "When switching between issues" + + location: "Session plan.md in session-state folder" +``` + +### 18.18 GitHub CLI Workarounds + +**Known issues with `gh` CLI and workarounds:** + +```yaml +gh_cli_workarounds: + gh_pr_edit_body_unreliable: + problem: "`gh pr edit --body-file` sometimes fails silently" + symptom: "Exit code 1 with deprecation warning, body not updated" + workaround: | + # Use gh api directly instead: + gh api repos/{owner}/{repo}/pulls/{pr_number} -X PATCH -F body=@body.md + example: | + gh api repos/Azure/azure-cosmos-dotnet-v3/pulls/5597 -X PATCH -F body=@pr_body.md + + gh_pr_edit_title_unreliable: + problem: "`gh pr edit --title` may fail with GraphQL warning" + workaround: | + gh api repos/{owner}/{repo}/pulls/{pr_number} -X PATCH -f title="New Title" + + pr_body_formatting_corrupted: + problem: "PR description loses newlines, emojis corrupted (e.g., 🤖 becomes Γëí╞Æ├▒├╗)" + symptoms: + - "All content appears on single line" + - "Markdown headers not rendered" + - "Unicode characters corrupted" + causes: + - "Using Out-File without proper encoding" + - "PowerShell string handling stripping newlines" + - "gh pr create/edit with inline --body argument" + workaround: | + # 1. Write body to file with UTF8 no BOM encoding: + $body | Set-Content -Path "body.md" -Encoding UTF8 + + # 2. Use gh api with -F to read from file: + gh api repos/{owner}/{repo}/pulls/{pr_number} -X PATCH -F body=@body.md + + # 3. For new PRs, use --body-file (not --body): + gh pr create --draft --title "..." --body-file body.md + verification: | + # After update, verify formatting preserved: + gh api repos/{owner}/{repo}/pulls/{pr_number} --jq '.body' | Select-Object -First 5 + # Should show proper line breaks and headers + prevention: + - "NEVER use inline --body with multi-line content" + - "ALWAYS write to file first, then use -F body=@file or --body-file" + - "ALWAYS use UTF8 encoding (Set-Content -Encoding UTF8)" + - "VERIFY formatting after every PR create/update" +``` + +### 18.19 Sequence Diagrams in PRs + +**Add Mermaid sequence diagrams to help reviewers understand code flow:** + +```yaml +sequence_diagrams: + when_to_add: + - "Race conditions or timing-sensitive code" + - "Multi-component interactions (client → handler → service)" + - "Before/after behavior changes" + - "Error propagation paths" + + format: | + ```mermaid + sequenceDiagram + participant A as Component A + participant B as Component B + + A->>B: Method call + B-->>A: Return value + A--xB: Exception thrown + ``` + + example_before_after: | + ## Sequence Diagram + + ### Before Fix + ```mermaid + sequenceDiagram + App->>Client: Request() + Client--xApp: ❌ Confusing error + ``` + + ### After Fix + ```mermaid + sequenceDiagram + App->>Client: Request() + Client--xApp: ❌ Clear ObjectDisposedException + ``` + + benefits: + - "Reviewers understand flow at a glance" + - "Documents race condition scenarios" + - "Renders natively in GitHub markdown" +``` + +### 18.20 Review Feedback Workflow + +**Handling PR review comments efficiently:** + +```yaml +review_feedback_workflow: + on_comment_received: + step_1: "Read and understand the feedback" + step_2: "Implement the fix locally" + step_3: "Build and test" + step_4: "Commit with descriptive message" + step_5: "Push to PR branch" + step_6: "Reply to comment with commit SHA" + + reply_format: | + Good catch! Fixed in commit {sha}. + + **Change:** {brief description of what changed} + + multiple_comments: + approach: "Address all comments in one commit if related" + separate_if: "Comments are unrelated or complex" + + disagreeing_with_feedback: + approach: "Reply explaining your reasoning" + be_open: "Consider reviewer's perspective" + escalate_if: "Fundamental design disagreement" + + requesting_re_review: + after: "All comments addressed and pushed" + command: "gh pr edit {number} --add-reviewer {reviewer}" +``` + +--- + +## TODO: Implementation Tasks + +### Completed ✅ +- [x] Test plan on real issue (#5547) +- [x] Document branch naming convention (users//copilot--) +- [x] Add remote CI validation workflow (Section 7.4) +- [x] Document Copilot-authored PR format +- [x] Add lessons learned section +- [x] Add MCP server setup instructions (Section 0) +- [x] Document Azure DevOps MCP for CI retry (Section 16.5.1) +- [x] Add flaky test registry (Section 16.7) +- [x] Add draft PR workflow (Section 16.8) +- [x] Add parallel agent strategy (Section 16.9) +- [x] Add investigation document template (Section 16.10) +- [x] Add commit message format (Section 16.11) +- [x] Add PR description template (Section 16.12) + +### Pending +- [ ] Create GitHub Action workflow for auto-triggering agent on new issues +- [ ] Set up emulator environment in CI for reproduction tests +- [ ] Define reviewer assignment rules in CODEOWNERS +- [ ] Create issue templates with required fields +- [ ] Build integration tests for agent workflow +- [ ] Document agent capabilities for contributors +- [ ] Set up metrics dashboard +- [ ] Create Microsoft Docs feedback automation +- [ ] Define XML documentation linting rules +- [ ] Audit high-priority APIs for AI-friendly docs +- [ ] Create documentation improvement backlog +- [ ] Add "copilot-authored" label to repository diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..61927555b0 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,58 @@ + +# Copilot / AI assistant instructions — Microsoft.Azure.Cosmos + +Purpose: quick, actionable context so an AI coding assistant can be immediately productive in this repo. + +- **Big picture**: This repository implements the v3 .NET SDK for Azure Cosmos DB. Major components: + - `Microsoft.Azure.Cosmos/` — core SDK client, most production code. + - `Microsoft.Azure.Cosmos.Encryption/` and `Microsoft.Azure.Cosmos.Encryption.Custom/` — client-side encryption extensions. + - `Microsoft.Azure.Cosmos.Samples/` — runnable examples and usage patterns. + - `docs/`, `templates/`, and top-level Azure Pipelines YAMLs — CI, packaging and emulator setup. + +- **Why this structure**: the SDK core is separated from optional features (encryption, fault-injection, direct mode) so consumers can opt into smaller packages. Versioning and feature flags are centralized in `Directory.Build.props`. + +- **Build & test (most common workflows)**: + - Build solution: `dotnet build Microsoft.Azure.Cosmos.sln -c Release` (or simply `dotnet build`). + - Run unit/integration tests: `dotnet test --no-build` in the solution or specific test project folders under `**/tests/`. + - CI uses the YAML files in the repository root and `templates/` — see `templates/emulator-setup.yml` for the Windows emulator script used in CI. + +- **Local emulator and integration testing**: + - The codebase expects the Windows Cosmos DB Emulator in many integration tests. CI installs/starts it via `templates/emulator-setup.yml` (PowerShell scripts that download and launch the MSI and call `Start-Process CosmosDB.Emulator.exe`). + - If running tests locally on Windows, install the emulator and ensure exclusions and local state paths match what's in `templates/emulator-setup.yml`. + +- **Versioning & build flags**: + - `Directory.Build.props` (repo root and project-level overrides) contains the canonical package versions and MSBuild flags (e.g. ``, ``, and `DefineConstants` that add `PREVIEW`/`ENCRYPTIONPREVIEW`). + - Feature/preview builds are gated by MSBuild properties like `IsPreview` or `IsNightly`; set these via `dotnet msbuild /p:IsPreview=true` when needed. + +- **Conventions & patterns** (project-specific) + - Avoid introducing new global build properties; add versions to `Directory.Build.props` where applicable. + - Tests use the emulator or mocks; integration tests that depend on emulator are usually under `tests/` and expect environment-based setup. Look for CI templates for exact start-up sequence. + - Strong-name signing keys exist at repo root (`35MSSharedLib1024.snk`, `testkey.snk`); builds may require signing configuration on CI. + +- **Integration points & external deps**: + - Azure Cosmos DB Emulator (Windows) — required for many integration tests. + - NuGet packaging and pipeline tooling — see `templates/nuget-pack.yml` and the many `azure-pipelines-*.yml` files for packaging/release behavior. + +- **Where to look for examples** (use these as source-of-truth snippets): + - `Directory.Build.props` — versioning and define-constants + - `templates/emulator-setup.yml` — exact emulator install/start PowerShell used in CI + - `Microsoft.Azure.Cosmos/` — core SDK patterns (public APIs, partitioning, feed iterator usage) + - `Microsoft.Azure.Cosmos.Samples/` — minimal runnable samples for usage patterns + +- **How AI should produce code/changes here**: + - **🚫 HARD RULE: NEVER push directly to `master` — NO EXCEPTIONS.** Always create a feature branch and submit a pull request. This rule cannot be overridden. + - Keep changes minimal and focused; prefer small, targeted edits and follow existing code style. + - When suggesting build/test changes, reference the relevant MSBuild property or pipeline YAML (point to `Directory.Build.props` or `templates/*`). + - Do not change version numbers or packaging settings without explicit instruction — these are centrally managed. + - If adding or modifying tests that require the emulator, include/update relevant CI/template steps and document required environment variables. + +- **Quick examples to reference in suggestions**: + - Use `FeedIterator` patterns as in `Microsoft.Azure.Cosmos` when generating query examples. + - For emulator-driven tests, mirror the startup sequence from `templates/emulator-setup.yml`. + +- **Named Copilot Agents**: + - **IssueFixAgent** (`.github/agents/issue-fix-agent.agent.md`) — Comprehensive workflow for triaging and fixing GitHub issues. Use `@IssueFixAgent` in VS Code Copilot Chat or follow the plan manually. + - Includes: environment setup, issue investigation, fix implementation, testing requirements, PR workflow, and learnings capture. + - Start with: `@IssueFixAgent investigate issue #XXXX` or see Quick Start in the agent file. + +If anything here is unclear or you want the file to include additional examples (specific files, common refactor targets, or typical PR reviewers), tell me what to add and I will iterate. diff --git a/.gitignore b/.gitignore index 3e759b75bf..85b4de0d14 100644 --- a/.gitignore +++ b/.gitignore @@ -326,5 +326,11 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ + +# Local scratch/test projects (not part of the SDK) +ChangeFeedWithFaultInjection/ +ConsoleApp1/ +ItemGenerator/ +Backup/ \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..bb76300714 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-azuretools.vscode-azurefunctions", + "ms-dotnettools.csharp" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..894cbe6aab --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to .NET Functions", + "type": "coreclr", + "request": "attach", + "processId": "${command:azureFunctions.pickProcess}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..ffea2f4755 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "azureFunctions.deploySubpath": "Microsoft.Azure.Cosmos.Samples\\Usage\\AzureFunctions/bin/Release/net6.0/publish", + "azureFunctions.projectLanguage": "C#", + "azureFunctions.projectRuntime": "~4", + "debug.internalConsoleOptions": "neverOpen", + "azureFunctions.preDeployTask": "publish (functions)" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..b7db94e048 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,81 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "clean (functions)", + "command": "dotnet", + "args": [ + "clean", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/Microsoft.Azure.Cosmos.Samples\\Usage\\AzureFunctions" + } + }, + { + "label": "build (functions)", + "command": "dotnet", + "args": [ + "build", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "clean (functions)", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/Microsoft.Azure.Cosmos.Samples\\Usage\\AzureFunctions" + } + }, + { + "label": "clean release (functions)", + "command": "dotnet", + "args": [ + "clean", + "--configuration", + "Release", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/Microsoft.Azure.Cosmos.Samples\\Usage\\AzureFunctions" + } + }, + { + "label": "publish (functions)", + "command": "dotnet", + "args": [ + "publish", + "--configuration", + "Release", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "clean release (functions)", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/Microsoft.Azure.Cosmos.Samples\\Usage\\AzureFunctions" + } + }, + { + "type": "func", + "dependsOn": "build (functions)", + "options": { + "cwd": "${workspaceFolder}/Microsoft.Azure.Cosmos.Samples\\Usage\\AzureFunctions/bin/Debug/net6.0" + }, + "command": "host start", + "isBackground": true, + "problemMatcher": "$func-dotnet-watch" + } + ] +} \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 7aa6ac0668..015f0e14cc 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,9 +1,9 @@ - 3.52.1 - 3.53.0 - preview.1 - 3.39.1 + 3.57.0 + 3.58.0 + preview.0 + 3.41.3 1.0.0 beta.0 2.0.5 diff --git a/Exceptions.md b/Exceptions.md index 5414009567..c3c34059bf 100644 --- a/Exceptions.md +++ b/Exceptions.md @@ -33,3 +33,60 @@ Cosmos DB SDK on any IO failure will attempt to retry the failed operation if re ## Common error status codes and troubleshooting guide To see a list of common error code and issues please see [.NET SDK troubleshooting guide](https://docs.microsoft.com/azure/cosmos-db/troubleshoot-dot-net-sdk) + +### Disambiguating 404 (Not Found) errors using SubStatusCode + +When you receive a 404 (Not Found) status code from Cosmos DB, it can indicate two different scenarios: +1. **Item not found**: The requested item doesn't exist in the container +2. **Owner resource not found**: The parent resource (container or database) doesn't exist + +To distinguish between these cases, check the `SubStatusCode` property: + +- **SubStatusCode 0**: Regular item not found (the item doesn't exist in an existing container) +- **SubStatusCode 1003**: Owner resource not found (the container or database doesn't exist) + +#### Example with Typed APIs (throws CosmosException): + +```csharp +try +{ + ItemResponse response = await container.ReadItemAsync("itemId", new PartitionKey("partitionKey")); +} +catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) +{ + if (ex.SubStatusCode == 1003) + { + // The container or database doesn't exist + Console.WriteLine("Owner resource (container/database) not found"); + } + else + { + // The item doesn't exist in an existing container + Console.WriteLine("Item not found"); + } +} +``` + +#### Example with Stream APIs (returns ResponseMessage): + +```csharp +ResponseMessage response = await container.ReadItemStreamAsync("itemId", new PartitionKey("partitionKey")); + +if (response.StatusCode == HttpStatusCode.NotFound) +{ + int subStatusCode = (int)response.Headers.SubStatusCode; + + if (subStatusCode == 1003) + { + // The container or database doesn't exist + Console.WriteLine("Owner resource (container/database) not found"); + } + else + { + // The item doesn't exist in an existing container + Console.WriteLine("Item not found"); + } +} +``` + +This distinction is particularly useful when implementing retry logic or error handling strategies, as you may want to handle these scenarios differently (e.g., creating the container if it doesn't exist vs. handling a missing item). diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/AeadAes/SymmetricKey.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/AeadAes/SymmetricKey.cs index a6f15a5197..ea20b9e556 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/AeadAes/SymmetricKey.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/AeadAes/SymmetricKey.cs @@ -23,10 +23,11 @@ internal class SymmetricKey /// root key internal SymmetricKey(byte[] rootKey) { - // Key validation - if (rootKey == null || rootKey.Length == 0) + ArgumentValidation.ThrowIfNull(rootKey); + + if (rootKey.Length == 0) { - throw new ArgumentNullException(nameof(rootKey)); + throw new ArgumentException("The root key cannot be empty.", nameof(rootKey)); } this.rootKey = rootKey; diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/CosmosDiagnosticsContext.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/CosmosDiagnosticsContext.cs index 308cf17f8b..4c5a0ae90c 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/CosmosDiagnosticsContext.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/CosmosDiagnosticsContext.cs @@ -5,32 +5,84 @@ namespace Microsoft.Azure.Cosmos.Encryption.Custom { using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Threading; /// - /// This is an empty implementation of CosmosDiagnosticsContext which has been plumbed through the DataEncryptionKeyProvider and EncryptionContainer. - /// This may help adding diagnostics more easily in future. + /// Lightweight diagnostics context for Custom Encryption extension. + /// Manages Activity creation for OpenTelemetry integration. /// internal class CosmosDiagnosticsContext { - private static readonly CosmosDiagnosticsContext UnusedSingleton = new (); - private static readonly IDisposable UnusedScopeSingleton = new Scope(); + private static readonly ActivitySource ActivitySource = new ("Microsoft.Azure.Cosmos.Encryption.Custom"); + /// + /// Scope name prefix for MDE (Microsoft.Data.Encryption) encrypt operations. + /// + internal const string ScopeEncryptModeSelectionPrefix = "EncryptionProcessor.Encrypt.Mde."; + + /// + /// Scope name prefix for MDE (Microsoft.Data.Encryption) decrypt operations. + /// + internal const string ScopeDecryptModeSelectionPrefix = "EncryptionProcessor.Decrypt.Mde."; + + internal CosmosDiagnosticsContext() + { + } + + /// + /// Creates a new diagnostics context instance. + /// public static CosmosDiagnosticsContext Create(RequestOptions options) { _ = options; - return CosmosDiagnosticsContext.UnusedSingleton; + return new CosmosDiagnosticsContext(); } - public IDisposable CreateScope(string scope) + /// + /// Creates a new diagnostic scope for Activity tracking. + /// + /// The name of the scope. + /// A that manages an Activity lifecycle. + /// Thrown when is null. + /// Thrown when is empty or whitespace. + /// + /// Use with a using statement to ensure proper disposal. + /// + public Scope CreateScope(string scope) { - _ = scope; - return CosmosDiagnosticsContext.UnusedScopeSingleton; + ArgumentValidation.ThrowIfNullOrWhiteSpace(scope, nameof(scope)); + + Activity activity = ActivitySource.HasListeners() ? ActivitySource.StartActivity(scope, ActivityKind.Internal) : null; + + return new Scope(activity); } - private class Scope : IDisposable + /// + /// Represents a diagnostic scope for Activity tracking. + /// Must be used with the 'using' pattern to ensure proper disposal. + /// + /// + /// Dispose() is idempotent - calling it multiple times will only dispose the Activity once. + /// + public sealed class Scope : IDisposable { + private readonly Activity activity; + private bool isDisposed; + + internal Scope(Activity activity) + { + this.activity = activity; + } + public void Dispose() { + if (!this.isDisposed) + { + this.isDisposed = true; + this.activity?.Dispose(); + } } } } diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/CosmosJsonDotNetSerializer.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/CosmosJsonDotNetSerializer.cs index 9ed8056360..e0b7e1121f 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/CosmosJsonDotNetSerializer.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/CosmosJsonDotNetSerializer.cs @@ -40,14 +40,7 @@ internal CosmosJsonDotNetSerializer(JsonSerializerSettings jsonSerializerSetting /// The object representing the deserialized stream public T FromStream(Stream stream) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(stream); -#else - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } -#endif + ArgumentValidation.ThrowIfNull(stream); if (typeof(Stream).IsAssignableFrom(typeof(T))) { @@ -87,6 +80,41 @@ public MemoryStream ToStream(T input) return streamPayload; } + /// + /// Serializes an object directly into the provided output stream (which remains open). + /// + /// Type of object being serialized. + /// Object to serialize. + /// Destination stream. Must be writable. The stream is not disposed by this method. + /// Thrown when is null. + /// Thrown when is not writable. + /// + /// This method serializes the object directly to the provided stream without creating an intermediate MemoryStream, + /// reducing memory allocations for large objects. + /// After writing, the stream position will be at the end of the written content. + /// Callers are responsible for resetting the stream position if needed for subsequent reads. + /// + public void WriteToStream(T input, Stream output) + { + ArgumentValidation.ThrowIfNull(output); + + if (!output.CanWrite) + { + throw new ArgumentException("Output stream must be writable", nameof(output)); + } + + using (StreamWriter streamWriter = new (output, encoding: CosmosJsonDotNetSerializer.DefaultEncoding, bufferSize: 1024, leaveOpen: true)) + using (JsonTextWriter writer = new (streamWriter)) + { + writer.ArrayPool = JsonArrayPool.Instance; + writer.Formatting = Newtonsoft.Json.Formatting.None; + JsonSerializer jsonSerializer = this.GetSerializer(); + jsonSerializer.Serialize(writer, input); + writer.Flush(); + streamWriter.Flush(); + } + } + /// /// JsonSerializer has hit a race conditions with custom settings that cause null reference exception. /// To avoid the race condition a new JsonSerializer is created for each call diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/StreamExtensions.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/StreamExtensions.cs new file mode 100644 index 0000000000..24305569b9 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Common/StreamExtensions.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom +{ + using System.IO; + using System.Threading.Tasks; + + /// + /// Extension methods for Stream to provide compatibility across different .NET versions. + /// + internal static class StreamExtensions + { + /// + /// Asynchronously disposes the stream in a version-compatible way. + /// Uses DisposeAsync on .NET 8.0+ and falls back to synchronous Dispose on earlier versions. + /// + /// The stream to dispose. + /// A ValueTask representing the asynchronous dispose operation. + public static ValueTask DisposeCompatAsync(this Stream stream) + { +#if NET8_0_OR_GREATER + return stream.DisposeAsync(); +#else + stream.Dispose(); + return default; +#endif + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/CosmosDataEncryptionKeyProvider.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/CosmosDataEncryptionKeyProvider.cs index c6f1ffb66c..78c7b52c96 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/CosmosDataEncryptionKeyProvider.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/CosmosDataEncryptionKeyProvider.cs @@ -148,14 +148,7 @@ public async Task InitializeAsync( throw new InvalidOperationException($"{nameof(CosmosDataEncryptionKeyProvider)} has already been initialized."); } -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(database); -#else - if (database == null) - { - throw new ArgumentNullException(nameof(database)); - } -#endif + ArgumentValidation.ThrowIfNull(database); ContainerResponse containerResponse = await database.CreateContainerIfNotExistsAsync( containerId, diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKey.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKey.cs index 810227479e..fc3f65b816 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKey.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKey.cs @@ -102,14 +102,7 @@ public static DataEncryptionKey Create( byte[] rawKey, string encryptionAlgorithm) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(rawKey); -#else - if (rawKey == null) - { - throw new ArgumentNullException(nameof(rawKey)); - } -#endif + ArgumentValidation.ThrowIfNull(rawKey); #pragma warning disable CS0618 // Type or member is obsolete if (!string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized)) diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKeyContainerCore.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKeyContainerCore.cs index 1fcdd8c783..9768359f3c 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKeyContainerCore.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKeyContainerCore.cs @@ -57,24 +57,14 @@ public override async Task> CreateData ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - if (string.IsNullOrEmpty(id)) - { - throw new ArgumentNullException(nameof(id)); - } + ArgumentValidation.ThrowIfNullOrEmpty(id); if (!CosmosEncryptionAlgorithm.VerifyIfSupportedAlgorithm(encryptionAlgorithm)) { throw new ArgumentException(string.Format("Unsupported Encryption Algorithm {0}", encryptionAlgorithm), nameof(encryptionAlgorithm)); } -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(encryptionKeyWrapMetadata); -#else - if (encryptionKeyWrapMetadata == null) - { - throw new ArgumentNullException(nameof(encryptionKeyWrapMetadata)); - } -#endif + ArgumentValidation.ThrowIfNull(encryptionKeyWrapMetadata); CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions); @@ -94,7 +84,7 @@ public override async Task> CreateData } else if (string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, StringComparison.Ordinal)) { - (wrappedDek, updatedMetadata) = this.GenerateAndWrapPdekForMdeEncAlgo(id, encryptionKeyWrapMetadata); + (wrappedDek, updatedMetadata) = await this.GenerateAndWrapPdekForMdeEncAlgoAsync(id, encryptionKeyWrapMetadata, cancellationToken); } #pragma warning restore CS0618 // Type or member is obsolete @@ -159,14 +149,7 @@ public override async Task> RewrapData ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(newWrapMetadata); -#else - if (newWrapMetadata == null) - { - throw new ArgumentNullException(nameof(newWrapMetadata)); - } -#endif + ArgumentValidation.ThrowIfNull(newWrapMetadata); CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions); @@ -403,7 +386,7 @@ internal async Task FetchUnwrappedAsync( { if (string.Equals(dekProperties.EncryptionAlgorithm, CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, StringComparison.Ordinal)) { - DataEncryptionKey dek = this.InitMdeEncryptionAlgorithm(dekProperties, withRawKey); + DataEncryptionKey dek = await this.InitMdeEncryptionAlgorithmAsync(dekProperties, withRawKey, cancellationToken); // TTL is not used since DEK is not cached. return new InMemoryRawDek(dek, TimeSpan.FromMilliseconds(0)); @@ -546,7 +529,7 @@ internal async Task UnwrapAsync( cancellationToken); } - private (byte[], EncryptionKeyWrapMetadata) GenerateAndWrapPdekForMdeEncAlgo(string id, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata) + private async Task<(byte[], EncryptionKeyWrapMetadata)> GenerateAndWrapPdekForMdeEncAlgoAsync(string id, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata, CancellationToken cancellationToken) { if (this.DekProvider.MdeKeyWrapProvider == null) { @@ -559,9 +542,10 @@ internal async Task UnwrapAsync( encryptionKeyWrapMetadata.Value, this.DekProvider.MdeKeyWrapProvider.EncryptionKeyStoreProvider); - ProtectedDataEncryptionKey protectedDataEncryptionKey = new ( + ProtectedDataEncryptionKey protectedDataEncryptionKey = await ProtectedDataEncryptionKey.CreateAsync( id, - keyEncryptionKey); + keyEncryptionKey, + cancellationToken).ConfigureAwait(false); byte[] wrappedDek = protectedDataEncryptionKey.EncryptedValue; EncryptionKeyWrapMetadata updatedMetadata = encryptionKeyWrapMetadata; @@ -591,7 +575,7 @@ private async Task UnWrapDekMdeEncAlgoAsync( return unwrapResult; } - internal DataEncryptionKey InitMdeEncryptionAlgorithm(DataEncryptionKeyProperties dekProperties, bool withRawKey = false) + internal async Task InitMdeEncryptionAlgorithmAsync(DataEncryptionKeyProperties dekProperties, bool withRawKey, CancellationToken cancellationToken) { if (this.DekProvider.MdeKeyWrapProvider == null) { @@ -599,12 +583,13 @@ internal DataEncryptionKey InitMdeEncryptionAlgorithm(DataEncryptionKeyPropertie "Encryptor or CosmosDataEncryptionKeyProvider needs to be initialized with EncryptionKeyStoreProvider."); } - return new MdeEncryptionAlgorithm( + return await MdeEncryptionAlgorithm.CreateAsync( dekProperties, Data.Encryption.Cryptography.EncryptionType.Randomized, this.DekProvider.MdeKeyWrapProvider.EncryptionKeyStoreProvider, this.DekProvider.PdekCacheTimeToLive, - withRawKey); + withRawKey, + cancellationToken); } private async Task ReadResourceAsync( diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKeyContainerInlineCore.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKeyContainerInlineCore.cs index bab5cadd62..385819ab97 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKeyContainerInlineCore.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/DataEncryptionKeyContainerInlineCore.cs @@ -56,10 +56,7 @@ public override Task> ReadDataEncrypti ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - if (string.IsNullOrEmpty(id)) - { - throw new ArgumentNullException(nameof(id)); - } + ArgumentValidation.ThrowIfNullOrEmpty(id); return TaskHelper.RunInlineIfNeededAsync(() => this.dataEncryptionKeyContainerCore.ReadDataEncryptionKeyAsync(id, requestOptions, cancellationToken)); @@ -73,19 +70,8 @@ public override Task> RewrapDataEncryp ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - if (string.IsNullOrEmpty(id)) - { - throw new ArgumentNullException(nameof(id)); - } - -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(newWrapMetadata); -#else - if (newWrapMetadata == null) - { - throw new ArgumentNullException(nameof(newWrapMetadata)); - } -#endif + ArgumentValidation.ThrowIfNullOrEmpty(id); + ArgumentValidation.ThrowIfNull(newWrapMetadata); return TaskHelper.RunInlineIfNeededAsync(() => this.dataEncryptionKeyContainerCore.RewrapDataEncryptionKeyAsync(id, newWrapMetadata, encryptionAlgorithm, requestOptions, cancellationToken)); diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/DecryptableFeedResponse.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/DecryptableFeedResponse.cs index 24c07c71e4..1b8d1e94d5 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/DecryptableFeedResponse.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/DecryptableFeedResponse.cs @@ -45,14 +45,7 @@ internal static DecryptableFeedResponse CreateResponse( ResponseMessage responseMessage, IReadOnlyCollection resource) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(responseMessage); -#else - if (responseMessage == null) - { - throw new ArgumentNullException(nameof(responseMessage)); - } -#endif + ArgumentValidation.ThrowIfNull(responseMessage); using (responseMessage) { diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionContainer.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionContainer.cs index f769e1504a..48615d818b 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionContainer.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionContainer.cs @@ -51,10 +51,7 @@ public override async Task> CreateItemAsync( ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentValidation.ThrowIfNull(item); if (requestOptions is not EncryptionItemRequestOptions encryptionItemRequestOptions || encryptionItemRequestOptions.EncryptionOptions == null) @@ -122,14 +119,7 @@ public override async Task CreateItemStreamAsync( ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(streamPayload); -#else - if (streamPayload == null) - { - throw new ArgumentNullException(nameof(streamPayload)); - } -#endif + ArgumentValidation.ThrowIfNull(streamPayload); CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions); using (diagnosticsContext.CreateScope("CreateItemStream")) @@ -165,7 +155,7 @@ private async Task CreateItemHelperAsync( streamPayload = await EncryptionProcessor.EncryptAsync( streamPayload, this.Encryptor, - encryptionItemRequestOptions.EncryptionOptions, + encryptionItemRequestOptions, diagnosticsContext, cancellationToken); @@ -181,6 +171,7 @@ private async Task CreateItemHelperAsync( responseMessage.Content, this.Encryptor, diagnosticsContext, + requestOptions, cancellationToken); } @@ -295,6 +286,7 @@ private async Task ReadItemHelperAsync( responseMessage.Content, this.Encryptor, diagnosticsContext, + requestOptions, cancellationToken); } @@ -308,20 +300,8 @@ public override async Task> ReplaceItemAsync( ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(id); - ArgumentNullException.ThrowIfNull(item); -#else - if (id == null) - { - throw new ArgumentNullException(nameof(id)); - } - - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } -#endif + ArgumentValidation.ThrowIfNull(id); + ArgumentValidation.ThrowIfNull(item); if (requestOptions is not EncryptionItemRequestOptions encryptionItemRequestOptions || encryptionItemRequestOptions.EncryptionOptions == null) @@ -393,20 +373,8 @@ public override async Task ReplaceItemStreamAsync( ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(id); - ArgumentNullException.ThrowIfNull(streamPayload); -#else - if (id == null) - { - throw new ArgumentNullException(nameof(id)); - } - - if (streamPayload == null) - { - throw new ArgumentNullException(nameof(streamPayload)); - } -#endif + ArgumentValidation.ThrowIfNull(id); + ArgumentValidation.ThrowIfNull(streamPayload); CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions); using (diagnosticsContext.CreateScope("ReplaceItemStream")) @@ -445,7 +413,7 @@ private async Task ReplaceItemHelperAsync( streamPayload = await EncryptionProcessor.EncryptAsync( streamPayload, this.Encryptor, - encryptionItemRequestOptions.EncryptionOptions, + encryptionItemRequestOptions, diagnosticsContext, cancellationToken); @@ -462,6 +430,7 @@ private async Task ReplaceItemHelperAsync( responseMessage.Content, this.Encryptor, diagnosticsContext, + requestOptions, cancellationToken); } @@ -474,10 +443,7 @@ public override async Task> UpsertItemAsync( ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentValidation.ThrowIfNull(item); if (requestOptions is not EncryptionItemRequestOptions encryptionItemRequestOptions || encryptionItemRequestOptions.EncryptionOptions == null) @@ -545,14 +511,7 @@ public override async Task UpsertItemStreamAsync( ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(streamPayload); -#else - if (streamPayload == null) - { - throw new ArgumentNullException(nameof(streamPayload)); - } -#endif + ArgumentValidation.ThrowIfNull(streamPayload); CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions); using (diagnosticsContext.CreateScope("UpsertItemStream")) @@ -588,7 +547,7 @@ private async Task UpsertItemHelperAsync( streamPayload = await EncryptionProcessor.EncryptAsync( streamPayload, this.Encryptor, - encryptionItemRequestOptions.EncryptionOptions, + encryptionItemRequestOptions, diagnosticsContext, cancellationToken); @@ -604,6 +563,7 @@ private async Task UpsertItemHelperAsync( responseMessage.Content, this.Encryptor, diagnosticsContext, + requestOptions, cancellationToken); } @@ -1010,7 +970,6 @@ public override async Task> ReadManyItemsAsync( return this.ResponseFactory.CreateItemFeedResponse(responseMessage); } -#if ENCRYPTIONPREVIEW public override Task> GetPartitionKeyRangesAsync( FeedRange feedRange, CancellationToken cancellationToken = default) @@ -1035,7 +994,6 @@ public override ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllV processorName, onChangesDelegate); } -#endif #if SDKPROJECTREF public override Task IsFeedRangePartOfAsync( @@ -1050,6 +1008,21 @@ public override Task IsFeedRangePartOfAsync( } #endif +#if PREVIEW && SDKPROJECTREF + public override Task SemanticRerankAsync( + string rerankContext, + IEnumerable documents, + IDictionary options = null, + CancellationToken cancellationToken = default) + { + return this.container.SemanticRerankAsync( + rerankContext, + documents, + options, + cancellationToken); + } +#endif + private async Task ReadManyItemsHelperAsync( IReadOnlyList<(string id, PartitionKey partitionKey)> items, ReadManyRequestOptions readManyRequestOptions = null, diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionFormatVersion.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionFormatVersion.cs index 035df18c61..5448f99f8b 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionFormatVersion.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionFormatVersion.cs @@ -8,6 +8,5 @@ internal static class EncryptionFormatVersion { public const int AeAes = 2; public const int Mde = 3; - public const int MdeWithCompression = 4; } } diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionOptions.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionOptions.cs index 525a8182c3..a5dff8f12b 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionOptions.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionOptions.cs @@ -6,25 +6,6 @@ namespace Microsoft.Azure.Cosmos.Encryption.Custom { using System.Collections.Generic; - /// - /// API for JSON processing - /// - public enum JsonProcessor - { - /// - /// Newtonsoft.Json - /// - Newtonsoft, - -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER - /// - /// Ut8JsonReader/Writer - /// - /// Available with .NET8.0 package only. - Stream, -#endif - } - /// /// Options for encryption of data. /// @@ -48,22 +29,11 @@ public sealed class EncryptionOptions /// public string EncryptionAlgorithm { get; set; } - /// - /// Gets or sets payload compression mode - /// - public CompressionOptions CompressionOptions { get; set; } = new CompressionOptions(); - /// /// Gets or sets list of JSON paths to encrypt on the payload. /// Only top level paths are supported. /// Example of a path specification: /sensitive /// public IEnumerable PathsToEncrypt { get; set; } - - /// - /// Gets or sets API used for Json processing - /// - /// Setting only applies with Mde encryption is used. - public JsonProcessor JsonProcessor { get; set; } = JsonProcessor.Newtonsoft; } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionOptionsExtensions.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionOptionsExtensions.cs index 09f02d71d0..604e9062f1 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionOptionsExtensions.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionOptionsExtensions.cs @@ -10,28 +10,13 @@ namespace Microsoft.Azure.Cosmos.Encryption.Custom internal static class EncryptionOptionsExtensions { - internal static void Validate(this EncryptionOptions options) + internal static void Validate(this EncryptionOptions options, JsonProcessor jsonProcessor) { - if (string.IsNullOrWhiteSpace(options.DataEncryptionKeyId)) - { -#pragma warning disable CA2208 // Instantiate argument exceptions correctly - throw new ArgumentNullException(nameof(options.DataEncryptionKeyId)); -#pragma warning restore CA2208 // Instantiate argument exceptions correctly - } - - if (string.IsNullOrWhiteSpace(options.EncryptionAlgorithm)) - { -#pragma warning disable CA2208 // Instantiate argument exceptions correctly - throw new ArgumentNullException(nameof(options.EncryptionAlgorithm)); -#pragma warning restore CA2208 // Instantiate argument exceptions correctly - } + ArgumentValidation.ThrowIfNullOrWhiteSpace(options.DataEncryptionKeyId, nameof(options.DataEncryptionKeyId)); + ArgumentValidation.ThrowIfNullOrWhiteSpace(options.EncryptionAlgorithm, nameof(options.EncryptionAlgorithm)); + ArgumentValidation.ThrowIfNull(options.PathsToEncrypt, nameof(options.PathsToEncrypt)); - if (options.PathsToEncrypt == null) - { -#pragma warning disable CA2208 // Instantiate argument exceptions correctly - throw new ArgumentNullException(nameof(options.PathsToEncrypt)); -#pragma warning restore CA2208 // Instantiate argument exceptions correctly - } + options.ValidateJsonProcessor(jsonProcessor); if (options.PathsToEncrypt is not HashSet && options.PathsToEncrypt.Distinct().Count() != options.PathsToEncrypt.Count()) { @@ -50,18 +35,18 @@ internal static void Validate(this EncryptionOptions options) throw new InvalidOperationException($"{nameof(options.PathsToEncrypt)} includes a invalid path: '{path}'."); } } - - options.CompressionOptions?.Validate(); } - internal static void Validate(this CompressionOptions options) + public static void ValidateJsonProcessor(this EncryptionOptions options, JsonProcessor jsonProcessor) { - if (options.MinimalCompressedLength < 0) +#if NET8_0_OR_GREATER +#pragma warning disable CS0618 // Type or member is obsolete + if (jsonProcessor != JsonProcessor.Newtonsoft && options.EncryptionAlgorithm == CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized) { -#pragma warning disable CA2208 // Instantiate argument exceptions correctly - throw new ArgumentOutOfRangeException(nameof(options.MinimalCompressedLength)); -#pragma warning restore CA2208 // Instantiate argument exceptions correctly + throw new NotSupportedException("JsonProcessor.Stream is not supported for AE AES encryption algorithm."); } +#pragma warning restore CS0618 // Type or member is obsolete +#endif } } } diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionProcessor.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionProcessor.cs index 7bf05fb930..d0efc48fe6 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionProcessor.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionProcessor.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ - namespace Microsoft.Azure.Cosmos.Encryption.Custom { using System; @@ -28,10 +27,6 @@ internal static class EncryptionProcessor internal static readonly CosmosJsonDotNetSerializer BaseSerializer = new (JsonSerializerSettings); -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER - private static readonly StreamProcessor StreamProcessor = new (); -#endif - private static readonly MdeEncryptionProcessor MdeEncryptionProcessor = new (); /// @@ -43,50 +38,82 @@ public static async Task EncryptAsync( Stream input, Encryptor encryptor, EncryptionOptions encryptionOptions, + JsonProcessor jsonProcessor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { - _ = diagnosticsContext; - ValidateInputForEncrypt( input, encryptor, - encryptionOptions); + encryptionOptions, + jsonProcessor); if (!encryptionOptions.PathsToEncrypt.Any()) { return input; } - #pragma warning disable CS0618 // Type or member is obsolete return encryptionOptions.EncryptionAlgorithm switch { - CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized => await MdeEncryptionProcessor.EncryptAsync(input, encryptor, encryptionOptions, cancellationToken), + CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized => await MdeEncryptionProcessor.EncryptAsync(input, encryptor, encryptionOptions, jsonProcessor, diagnosticsContext, cancellationToken), CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized => await AeAesEncryptionProcessor.EncryptAsync(input, encryptor, encryptionOptions, cancellationToken), _ => throw new NotSupportedException($"Encryption Algorithm : {encryptionOptions.EncryptionAlgorithm} is not supported."), }; #pragma warning restore CS0618 // Type or member is obsolete } -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER + public static Task EncryptAsync( + Stream input, + Encryptor encryptor, + EncryptionItemRequestOptions requestOptions, + CosmosDiagnosticsContext diagnosticsContext, + CancellationToken cancellationToken) + { + return EncryptAsync( + input, + encryptor, + requestOptions.EncryptionOptions, + requestOptions.GetJsonProcessor(), + diagnosticsContext, + cancellationToken); + } + + public static Task EncryptAsync( + Stream input, + Encryptor encryptor, + EncryptionTransactionalBatchItemRequestOptions requestOptions, + CosmosDiagnosticsContext diagnosticsContext, + CancellationToken cancellationToken) + { + return EncryptAsync( + input, + encryptor, + requestOptions.EncryptionOptions, + requestOptions.GetJsonProcessor(), + diagnosticsContext, + cancellationToken); + } + +#if NET8_0_OR_GREATER public static async Task EncryptAsync( Stream input, Stream output, Encryptor encryptor, EncryptionOptions encryptionOptions, + JsonProcessor jsonProcessor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { - _ = diagnosticsContext; - ValidateInputForEncrypt( input, encryptor, - encryptionOptions); + encryptionOptions, + jsonProcessor); if (!encryptionOptions.PathsToEncrypt.Any()) { await input.CopyToAsync(output, cancellationToken); + return; } @@ -95,12 +122,12 @@ public static async Task EncryptAsync( throw new NotSupportedException($"Streaming mode is only allowed for {nameof(CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized)}"); } - if (encryptionOptions.JsonProcessor != JsonProcessor.Stream) + if (jsonProcessor != JsonProcessor.Stream) { throw new NotSupportedException($"Streaming mode is only allowed for {nameof(JsonProcessor.Stream)}"); } - await EncryptionProcessor.StreamProcessor.EncryptStreamAsync(input, output, encryptor, encryptionOptions, cancellationToken); + await MdeEncryptionProcessor.EncryptAsync(input, output, encryptor, encryptionOptions, jsonProcessor, diagnosticsContext, cancellationToken); } #endif @@ -134,11 +161,8 @@ public static async Task EncryptAsync( } DecryptionContext decryptionContext = await DecryptInternalAsync(encryptor, diagnosticsContext, itemJObj, encryptionPropertiesJObj, cancellationToken); -#if NET8_0_OR_GREATER - await input.DisposeAsync(); -#else - input.Dispose(); -#endif + await input.DisposeCompatAsync(); + return (BaseSerializer.ToStream(itemJObj), decryptionContext); } @@ -146,84 +170,67 @@ public static async Task EncryptAsync( Stream input, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, - JsonProcessor jsonProcessor, - CancellationToken cancellationToken) - { - return jsonProcessor switch - { - JsonProcessor.Newtonsoft => await DecryptAsync(input, encryptor, diagnosticsContext, cancellationToken), -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER - JsonProcessor.Stream => await DecryptStreamAsync(input, encryptor, diagnosticsContext, cancellationToken), -#endif - _ => throw new InvalidOperationException("Unsupported Json Processor") - }; - } - -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER - public static async Task DecryptAsync( - Stream input, - Stream output, - Encryptor encryptor, - CosmosDiagnosticsContext diagnosticsContext, - JsonProcessor jsonProcessor, + RequestOptions requestOptions, CancellationToken cancellationToken) { if (input == null) { - return null; - } - - if (jsonProcessor != JsonProcessor.Stream) - { - throw new NotSupportedException($"Streaming mode is only allowed for {nameof(JsonProcessor.Stream)}"); + return (input, null); } Debug.Assert(input.CanSeek); - Debug.Assert(output.CanWrite); - Debug.Assert(output.CanSeek); Debug.Assert(encryptor != null); Debug.Assert(diagnosticsContext != null); - input.Position = 0; - EncryptionPropertiesWrapper properties = await System.Text.Json.JsonSerializer.DeserializeAsync(input, cancellationToken: cancellationToken); - input.Position = 0; - if (properties?.EncryptionProperties == null) + // Try to peek at the content to check if it's legacy encryption algorithm + // Some streams (e.g., those that only support async reads or contain malformed JSON) may throw exceptions + // during synchronous peeking. In such cases, delegate directly to MdeEncryptionProcessor. + try { - await input.CopyToAsync(output, cancellationToken: cancellationToken); - return null; - } + JObject itemJObj = RetrieveItem(input); + JObject encryptionPropertiesJObj = RetrieveEncryptionProperties(itemJObj); + + if (encryptionPropertiesJObj != null) + { + // Parse encryption properties to check the algorithm + EncryptionProperties encryptionProperties = encryptionPropertiesJObj.ToObject(); - DecryptionContext context; #pragma warning disable CS0618 // Type or member is obsolete - if (properties.EncryptionProperties.EncryptionAlgorithm == CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized) - { - context = await StreamProcessor.DecryptStreamAsync(input, output, encryptor, properties.EncryptionProperties, diagnosticsContext, cancellationToken); - } - else if (properties.EncryptionProperties.EncryptionAlgorithm == CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized) - { - (Stream stream, context) = await DecryptAsync(input, encryptor, diagnosticsContext, cancellationToken); - await stream.CopyToAsync(output, cancellationToken); - output.Position = 0; - } - else - { - input.Position = 0; - throw new NotSupportedException($"Encryption Algorithm: {properties.EncryptionProperties.EncryptionAlgorithm} is not supported."); - } + if (string.Equals(encryptionProperties.EncryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized, StringComparison.Ordinal)) #pragma warning restore CS0618 // Type or member is obsolete + { + // Use legacy decryption for AEAes256CbcHmacSha256Randomized + DecryptionContext decryptionContext = await DecryptInternalAsync(encryptor, diagnosticsContext, itemJObj, encryptionPropertiesJObj, cancellationToken); + await input.DisposeCompatAsync(); + return (BaseSerializer.ToStream(itemJObj), decryptionContext); + } + } - if (context == null) + // For MDE algorithm or no encryption properties, delegate to MdeEncryptionProcessor + input.Position = 0; + } + catch { + // Stream doesn't support synchronous reads, contains malformed JSON, or other parsing error. + // Reset position and delegate to MdeEncryptionProcessor which uses async reads and will handle errors appropriately. input.Position = 0; - return null; } - await input.DisposeAsync(); - return context; + return await MdeEncryptionProcessor.DecryptAsync(input, encryptor, diagnosticsContext, requestOptions, cancellationToken); + } + + public static async Task DecryptAsync( + Stream input, + Stream output, + Encryptor encryptor, + CosmosDiagnosticsContext diagnosticsContext, + RequestOptions requestOptions, + CancellationToken cancellationToken) + { + return await MdeEncryptionProcessor.DecryptAsync(input, output, encryptor, diagnosticsContext, requestOptions, cancellationToken); } -#endif -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +#if NET8_0_OR_GREATER public static async Task<(Stream, DecryptionContext)> DecryptStreamAsync( Stream input, Encryptor encryptor, @@ -249,7 +256,7 @@ public static async Task DecryptAsync( MemoryStream ms = new (); - DecryptionContext context = await StreamProcessor.DecryptStreamAsync(input, ms, encryptor, properties.EncryptionProperties, diagnosticsContext, cancellationToken); + DecryptionContext context = await MdeEncryptionProcessor.DecryptStreamAsync(input, ms, encryptor, properties.EncryptionProperties, diagnosticsContext, cancellationToken); if (context == null) { input.Position = 0; @@ -259,7 +266,6 @@ public static async Task DecryptAsync( await input.DisposeAsync(); return (ms, context); } - #endif public static async Task<(JObject, DecryptionContext)> DecryptAsync( @@ -325,30 +331,14 @@ internal static DecryptionContext CreateDecryptionContext( private static void ValidateInputForEncrypt( Stream input, Encryptor encryptor, - EncryptionOptions encryptionOptions) + EncryptionOptions encryptionOptions, + JsonProcessor jsonProcessor) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(input); - ArgumentNullException.ThrowIfNull(encryptor); - ArgumentNullException.ThrowIfNull(encryptionOptions); -#else - if (input == null) - { - throw new ArgumentNullException(nameof(input)); - } - - if (encryptor == null) - { - throw new ArgumentNullException(nameof(encryptor)); - } + ArgumentValidation.ThrowIfNull(input); + ArgumentValidation.ThrowIfNull(encryptor); + ArgumentValidation.ThrowIfNull(encryptionOptions); - if (encryptionOptions == null) - { - throw new ArgumentNullException(nameof(encryptionOptions)); - } -#endif - - encryptionOptions.Validate(); + encryptionOptions.Validate(jsonProcessor); } private static JObject RetrieveItem( @@ -356,21 +346,16 @@ private static JObject RetrieveItem( { Debug.Assert(input != null); - JObject itemJObj; - using (StreamReader sr = new (input, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true)) - using (JsonTextReader jsonTextReader = new (sr)) + using StreamReader sr = new (input, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true); + using JsonTextReader jsonTextReader = new (sr); + jsonTextReader.ArrayPool = JsonArrayPool.Instance; + JsonSerializerSettings jsonSerializerSettings = new () { - jsonTextReader.ArrayPool = JsonArrayPool.Instance; - JsonSerializerSettings jsonSerializerSettings = new () - { - DateParseHandling = DateParseHandling.None, - MaxDepth = 64, // https://github.com/advisories/GHSA-5crp-9r3c-p9vr - }; - - itemJObj = Newtonsoft.Json.JsonSerializer.Create(jsonSerializerSettings).Deserialize(jsonTextReader); - } + DateParseHandling = DateParseHandling.None, + MaxDepth = 64, // https://github.com/advisories/GHSA-5crp-9r3c-p9vr + }; - return itemJObj; + return Newtonsoft.Json.JsonSerializer.Create(jsonSerializerSettings).Deserialize(jsonTextReader); } private static JObject RetrieveEncryptionProperties( @@ -406,14 +391,11 @@ internal static async Task DeserializeAndDecryptResponseAsync( } CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(null); - using (diagnosticsContext.CreateScope("EncryptionProcessor.DeserializeAndDecryptResponseAsync")) - { - await DecryptAsync( - document, - encryptor, - diagnosticsContext, - cancellationToken); - } + await DecryptAsync( + document, + encryptor, + diagnosticsContext, + cancellationToken); } // the contents of contentJObj get decrypted in place for MDE algorithm model, and for legacy model _ei property is removed diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionTransactionalBatch.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionTransactionalBatch.cs index 0755ec0cf6..59e59bbc54 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionTransactionalBatch.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/EncryptionTransactionalBatch.cs @@ -61,7 +61,7 @@ public override TransactionalBatch CreateItemStream( streamPayload = EncryptionProcessor.EncryptAsync( streamPayload, this.encryptor, - encryptionItemRequestOptions.EncryptionOptions, + encryptionItemRequestOptions, diagnosticsContext, cancellationToken: default).Result; } @@ -133,7 +133,7 @@ public override TransactionalBatch ReplaceItemStream( streamPayload = EncryptionProcessor.EncryptAsync( streamPayload, this.encryptor, - encryptionItemRequestOptions.EncryptionOptions, + encryptionItemRequestOptions, diagnosticsContext, cancellationToken: default).Result; } @@ -180,7 +180,7 @@ public override TransactionalBatch UpsertItemStream( streamPayload = EncryptionProcessor.EncryptAsync( streamPayload, this.encryptor, - encryptionItemRequestOptions.EncryptionOptions, + encryptionItemRequestOptions, diagnosticsContext, cancellationToken: default).Result; } @@ -237,6 +237,7 @@ private async Task DecryptTransactionalBatchResponse result.ResourceStream, this.encryptor, diagnosticsContext, + requestOptions: null, cancellationToken); decryptedTransactionalBatchOperationResults.Add(new EncryptionTransactionalBatchOperationResult(result, decryptedStream)); diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/JsonProcessor.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/JsonProcessor.cs new file mode 100644 index 0000000000..35742a4421 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/JsonProcessor.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom +{ + /// + /// API for JSON processing + /// + internal enum JsonProcessor + { + /// + /// Newtonsoft.Json + /// + Newtonsoft, + +#if NET8_0_OR_GREATER + /// + /// Ut8JsonReader/Writer + /// + /// Available with .NET8.0 package only. + Stream, +#endif + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/JsonProcessorRequestOptionsExtensions.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/JsonProcessorRequestOptionsExtensions.cs new file mode 100644 index 0000000000..deef608d53 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/JsonProcessorRequestOptionsExtensions.cs @@ -0,0 +1,59 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom +{ + using System; + using Microsoft.Azure.Cosmos; + + /// + /// Provides extension methods for to configure JSON processor selection for encryption operations. + /// Centralizes handling of the JsonProcessor override communicated via . + /// + internal static class JsonProcessorRequestOptionsExtensions + { + /// + /// The property bag key used to store the JsonProcessor override in RequestOptions.Properties. + /// + internal const string JsonProcessorPropertyBagKey = "encryption-json-processor"; + + /// + /// Attempts to read a JsonProcessor override from the RequestOptions.Properties dictionary. + /// Supports both JsonProcessor enum values and string representations (case-insensitive). + /// + /// The request options to read from. + /// When this method returns, contains the JsonProcessor value if found; otherwise, JsonProcessor.Newtonsoft. + /// true if a valid JsonProcessor override was found; otherwise, false. + internal static bool TryReadJsonProcessorOverride(this RequestOptions requestOptions, out JsonProcessor jsonProcessor) + { + jsonProcessor = JsonProcessor.Newtonsoft; + if (requestOptions?.Properties != null && + requestOptions.Properties.TryGetValue(JsonProcessorPropertyBagKey, out object value) && value != null) + { + if (value is JsonProcessor enumVal) + { + jsonProcessor = enumVal; + return true; + } + else if (value is string s && Enum.TryParse(s, true, out JsonProcessor parsed)) + { + jsonProcessor = parsed; + return true; + } + } + + return false; + } + + internal static JsonProcessor GetJsonProcessor(this RequestOptions requestOptions, JsonProcessor defaultJsonProcessor = JsonProcessor.Newtonsoft) + { + if (requestOptions.TryReadJsonProcessorOverride(out JsonProcessor jsonProcessor)) + { + return jsonProcessor; + } + + return defaultJsonProcessor; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/MdeServices/MdeEncryptionAlgorithm.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/MdeServices/MdeEncryptionAlgorithm.cs index b35422d261..67f289e848 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/MdeServices/MdeEncryptionAlgorithm.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/MdeServices/MdeEncryptionAlgorithm.cs @@ -5,6 +5,8 @@ namespace Microsoft.Azure.Cosmos.Encryption.Custom { using System; + using System.Threading; + using System.Threading.Tasks; using Microsoft.Data.Encryption.Cryptography; /// @@ -27,52 +29,46 @@ internal sealed class MdeEncryptionAlgorithm : DataEncryptionKey /// here . /// More specifically this implements AEAD_AES_256_CBC_HMAC_SHA256 algorithm. /// - public MdeEncryptionAlgorithm( + public static async Task CreateAsync( DataEncryptionKeyProperties dekProperties, Data.Encryption.Cryptography.EncryptionType encryptionType, EncryptionKeyStoreProvider encryptionKeyStoreProvider, TimeSpan? cacheTimeToLive, - bool withRawKey = false) + bool withRawKey, + CancellationToken cancellationToken) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(dekProperties); - ArgumentNullException.ThrowIfNull(encryptionKeyStoreProvider); -#else - if (dekProperties == null) - { - throw new ArgumentNullException(nameof(dekProperties)); - } - - if (encryptionKeyStoreProvider == null) - { - throw new ArgumentNullException(nameof(encryptionKeyStoreProvider)); - } -#endif + ArgumentValidation.ThrowIfNull(dekProperties); + ArgumentValidation.ThrowIfNull(encryptionKeyStoreProvider); KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate( dekProperties.EncryptionKeyWrapMetadata.Name, dekProperties.EncryptionKeyWrapMetadata.Value, encryptionKeyStoreProvider); + AeadAes256CbcHmac256EncryptionAlgorithm aeadAes256CbcHmac256EncryptionAlgorithm; + byte[] rawKey = null; + if (!withRawKey) { ProtectedDataEncryptionKey protectedDataEncryptionKey = cacheTimeToLive.HasValue && cacheTimeToLive.Value == TimeSpan.Zero - ? new ProtectedDataEncryptionKey( + ? await ProtectedDataEncryptionKey.CreateAsync( dekProperties.Id, keyEncryptionKey, - dekProperties.WrappedDataEncryptionKey) - : ProtectedDataEncryptionKey.GetOrCreate( + dekProperties.WrappedDataEncryptionKey, + cancellationToken) + : await ProtectedDataEncryptionKey.GetOrCreateAsync( dekProperties.Id, keyEncryptionKey, - dekProperties.WrappedDataEncryptionKey); - this.mdeAeadAes256CbcHmac256EncryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate( + dekProperties.WrappedDataEncryptionKey, + cancellationToken); + aeadAes256CbcHmac256EncryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate( protectedDataEncryptionKey, encryptionType, Version); } else { - byte[] rawKey = keyEncryptionKey.DecryptEncryptionKey(dekProperties.WrappedDataEncryptionKey); + rawKey = await keyEncryptionKey.DecryptEncryptionKeyAsync(dekProperties.WrappedDataEncryptionKey, cancellationToken).ConfigureAwait(false); PlaintextDataEncryptionKey plaintextDataEncryptionKey = cacheTimeToLive.HasValue && (cacheTimeToLive.Value == TimeSpan.Zero) ? new PlaintextDataEncryptionKey( dekProperties.Id, @@ -80,12 +76,13 @@ public MdeEncryptionAlgorithm( : PlaintextDataEncryptionKey.GetOrCreate( dekProperties.Id, rawKey); - this.RawKey = rawKey; - this.mdeAeadAes256CbcHmac256EncryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate( + aeadAes256CbcHmac256EncryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate( plaintextDataEncryptionKey, encryptionType, Version); } + + return new MdeEncryptionAlgorithm(aeadAes256CbcHmac256EncryptionAlgorithm, rawKey); } /// @@ -106,6 +103,12 @@ public MdeEncryptionAlgorithm( Version); } + private MdeEncryptionAlgorithm(AeadAes256CbcHmac256EncryptionAlgorithm aeadAes256CbcHmac256EncryptionAlgorithm, byte[] rawKey) + { + this.mdeAeadAes256CbcHmac256EncryptionAlgorithm = aeadAes256CbcHmac256EncryptionAlgorithm ?? throw new ArgumentNullException(nameof(aeadAes256CbcHmac256EncryptionAlgorithm)); + this.RawKey = rawKey; + } + /// /// Encrypt data using EncryptionAlgorithm /// diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/MdeServices/MdeKeyWrapProvider.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/MdeServices/MdeKeyWrapProvider.cs index 297a4dac7e..e2bfb6d2f5 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/MdeServices/MdeKeyWrapProvider.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/MdeServices/MdeKeyWrapProvider.cs @@ -23,50 +23,38 @@ public MdeKeyWrapProvider(EncryptionKeyStoreProvider encryptionKeyStoreProvider) this.EncryptionKeyStoreProvider = encryptionKeyStoreProvider ?? throw new ArgumentNullException(nameof(encryptionKeyStoreProvider)); } - public override Task UnwrapKeyAsync( + public override async Task UnwrapKeyAsync( byte[] wrappedKey, EncryptionKeyWrapMetadata metadata, CancellationToken cancellationToken) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(metadata); -#else - if (metadata == null) - { - throw new ArgumentNullException(nameof(metadata)); - } -#endif + ArgumentValidation.ThrowIfNull(metadata); KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate( metadata.Name, metadata.Value, this.EncryptionKeyStoreProvider); - byte[] result = keyEncryptionKey.DecryptEncryptionKey(wrappedKey); - return Task.FromResult(new EncryptionKeyUnwrapResult(result, TimeSpan.Zero)); + byte[] result = await keyEncryptionKey.DecryptEncryptionKeyAsync(wrappedKey, cancellationToken).ConfigureAwait(false); + + return new EncryptionKeyUnwrapResult(result, TimeSpan.Zero); } - public override Task WrapKeyAsync( + public override async Task WrapKeyAsync( byte[] key, EncryptionKeyWrapMetadata metadata, CancellationToken cancellationToken) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(metadata); -#else - if (metadata == null) - { - throw new ArgumentNullException(nameof(metadata)); - } -#endif + ArgumentValidation.ThrowIfNull(metadata); KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate( metadata.Name, metadata.Value, this.EncryptionKeyStoreProvider); - byte[] result = keyEncryptionKey.EncryptEncryptionKey(key); - return Task.FromResult(new EncryptionKeyWrapResult(result, metadata)); + byte[] result = await keyEncryptionKey.EncryptEncryptionKeyAsync(key, cancellationToken).ConfigureAwait(false); + + return new EncryptionKeyWrapResult(result, metadata); } } } diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Microsoft.Azure.Cosmos.Encryption.Custom.csproj b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Microsoft.Azure.Cosmos.Encryption.Custom.csproj index 7477cc3ce3..1950d00c11 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Microsoft.Azure.Cosmos.Encryption.Custom.csproj +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Microsoft.Azure.Cosmos.Encryption.Custom.csproj @@ -18,17 +18,12 @@ true true microsoft;azure;cosmos;cosmosdb;documentdb;docdb;nosql;azureofficial;dotnetcore;netcore;netstandard;client;encryption;byok - $(DefineConstants);ENCRYPTION_CUSTOM_PREVIEW - - - - - + @@ -40,18 +35,11 @@ - - - - - - - - + - + diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/RentArrayBufferWriter.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/RentArrayBufferWriter.cs index 5b37ded4fb..2e276285ba 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/RentArrayBufferWriter.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/RentArrayBufferWriter.cs @@ -104,7 +104,7 @@ public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken { this.CheckIfDisposed(); - ArgumentNullException.ThrowIfNull(stream); + ArgumentValidation.ThrowIfNull(stream); await stream.WriteAsync(new Memory(this.rentedBuffer, 0, this.written), cancellationToken).ConfigureAwait(false); this.committed += this.written; @@ -116,7 +116,7 @@ public void CopyTo(Stream stream) { this.CheckIfDisposed(); - ArgumentNullException.ThrowIfNull(stream); + ArgumentValidation.ThrowIfNull(stream); stream.Write(this.rentedBuffer, 0, this.written); this.committed += this.written; diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/SecurityUtility.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/SecurityUtility.cs index 9a7ad82bbe..25cf0b2704 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/SecurityUtility.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/SecurityUtility.cs @@ -55,7 +55,6 @@ internal static string GetSHA256Hash(byte[] input) /// Buffer into which cryptographically random bytes are to be generated. internal static void GenerateRandomBytes(byte[] randomBytes) { - // Generate random bytes cryptographically. #if NET8_0_OR_GREATER RandomNumberGenerator.Fill(randomBytes); #else diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/IMdeJsonProcessorAdapter.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/IMdeJsonProcessorAdapter.cs new file mode 100644 index 0000000000..d510652521 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/IMdeJsonProcessorAdapter.cs @@ -0,0 +1,77 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation; + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +/// +/// Adapter interface that abstracts JSON processing for MDE encryption operations. +/// Enables pluggable JSON serialization implementations (Newtonsoft.Json, System.Text.Json streaming) +/// while providing a unified API for encrypt/decrypt operations in MdeEncryptionProcessor. +/// +/// +/// Implementations: +/// - NewtonsoftAdapter: Uses Newtonsoft.Json with JObject for in-memory processing +/// - SystemTextJsonStreamAdapter: Uses System.Text.Json Utf8JsonReader/Writer for streaming (NET8+) +/// +/// The adapter pattern allows runtime selection of JSON processors via EncryptionOptions.JsonProcessor +/// or RequestOptions property bag overrides, with lazy instantiation and caching in MdeEncryptionProcessor. +/// +internal interface IMdeJsonProcessorAdapter +{ + /// + /// Encrypts a JSON stream and returns the encrypted result as a new stream. + /// + /// Input stream containing JSON document to encrypt. + /// Encryptor to use for encryption operations. + /// Encryption options including paths to encrypt. + /// Cancellation token. + /// New stream containing encrypted JSON document. + Task EncryptAsync(Stream input, Encryptor encryptor, EncryptionOptions options, CancellationToken cancellationToken); + + /// + /// Encrypts a JSON stream and writes the encrypted result to an output stream. + /// Only supported by SystemTextJsonStreamAdapter for true streaming encryption. + /// + /// Input stream containing JSON document to encrypt. + /// Output stream to write encrypted JSON document to. + /// Encryptor to use for encryption operations. + /// Encryption options including paths to encrypt. + /// JSON processor. + /// Cancellation token. + /// Thrown by NewtonsoftAdapter as it requires in-memory processing. + Task EncryptAsync(Stream input, Stream output, Encryptor encryptor, EncryptionOptions options, JsonProcessor jsonProcessor, CancellationToken cancellationToken); + + /// + /// Decrypts a JSON stream and returns the decrypted result as a new stream along with decryption context. + /// + /// Input stream containing potentially encrypted JSON document. + /// Encryptor to use for decryption operations. + /// Diagnostics context for telemetry. + /// Cancellation token. + /// + /// Tuple containing: + /// - Stream: New stream with decrypted JSON (or original input if no MDE encryption detected) + /// - DecryptionContext: Metadata about decryption operation (null if document wasn't encrypted) + /// + Task<(Stream, DecryptionContext)> DecryptAsync(Stream input, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken); + + /// + /// Decrypts a JSON stream and writes the decrypted result to an output stream, returning decryption context. + /// + /// Input stream containing potentially encrypted JSON document. + /// Output stream to write decrypted JSON document to. + /// Encryptor to use for decryption operations. + /// Diagnostics context for telemetry. + /// Cancellation token. + /// + /// DecryptionContext with metadata about decryption operation (null if document wasn't encrypted). + /// If null is returned, input stream position is reset to 0 if seekable. + /// + Task DecryptAsync(Stream input, Stream output, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/JObjectSqlSerializer.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/JObjectSqlSerializer.cs new file mode 100644 index 0000000000..cc83be99db --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/JObjectSqlSerializer.cs @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation +{ + using System; + using System.Diagnostics; + using Microsoft.Data.Encryption.Cryptography.Serializers; + using Newtonsoft.Json; + using Newtonsoft.Json.Linq; + + internal class JObjectSqlSerializer + { + private static readonly SqlBitSerializer SqlBoolSerializer = new (); + private static readonly SqlFloatSerializer SqlDoubleSerializer = new (); + private static readonly SqlBigIntSerializer SqlLongSerializer = new (); + + // UTF-8 encoding. + private static readonly SqlVarCharSerializer SqlVarCharSerializer = new (size: -1, codePageCharacterEncoding: 65001); + + private static readonly JsonSerializerSettings JsonSerializerSettings = EncryptionProcessor.JsonSerializerSettings; + +#pragma warning disable SA1101 // Prefix local calls with this - false positive on SerializeFixed + internal virtual (TypeMarker typeMarker, byte[] serializedBytes, int serializedBytesCount) Serialize(JToken propertyValue, ArrayPoolManager arrayPoolManager) + { + byte[] buffer; + int length; + switch (propertyValue.Type) + { + case JTokenType.Undefined: + Debug.Assert(false, "Undefined value cannot be in the JSON"); + return (default, null, -1); + case JTokenType.Null: + Debug.Assert(false, "Null type should have been handled by caller"); + return (TypeMarker.Null, null, -1); + case JTokenType.Boolean: + (buffer, length) = SerializeFixed(SqlBoolSerializer); + return (TypeMarker.Boolean, buffer, length); + case JTokenType.Float: + (buffer, length) = SerializeFixed(SqlDoubleSerializer); + return (TypeMarker.Double, buffer, length); + case JTokenType.Integer: + (buffer, length) = SerializeFixed(SqlLongSerializer); + return (TypeMarker.Long, buffer, length); + case JTokenType.String: + (buffer, length) = SerializeString(propertyValue.ToObject()); + return (TypeMarker.String, buffer, length); + case JTokenType.Array: + (buffer, length) = SerializeString(propertyValue.ToString(Formatting.None)); + return (TypeMarker.Array, buffer, length); + case JTokenType.Object: + (buffer, length) = SerializeString(propertyValue.ToString(Formatting.None)); + return (TypeMarker.Object, buffer, length); + default: + throw new InvalidOperationException($" Invalid or Unsupported Data Type Passed : {propertyValue.Type}"); + } + + (byte[], int) SerializeFixed(IFixedSizeSerializer serializer) + { + byte[] buffer = arrayPoolManager.Rent(serializer.GetSerializedMaxByteCount()); + int length = serializer.Serialize(propertyValue.ToObject(), buffer); + return (buffer, length); + } + + (byte[], int) SerializeString(string value) + { + byte[] buffer = arrayPoolManager.Rent(SqlVarCharSerializer.GetSerializedMaxByteCount(value.Length)); + int length = SqlVarCharSerializer.Serialize(value, buffer); + return (buffer, length); + } + } + + internal virtual void DeserializeAndAddProperty( + TypeMarker typeMarker, + ReadOnlySpan serializedBytes, + JObject jObject, + string key, + ArrayPoolManager arrayPoolManager) + { + switch (typeMarker) + { + case TypeMarker.Boolean: + jObject[key] = SqlBoolSerializer.Deserialize(serializedBytes); + break; + case TypeMarker.Double: + jObject[key] = SqlDoubleSerializer.Deserialize(serializedBytes); + break; + case TypeMarker.Long: + jObject[key] = SqlLongSerializer.Deserialize(serializedBytes); + break; + case TypeMarker.String: + jObject[key] = SqlVarCharSerializer.Deserialize(serializedBytes); + break; + case TypeMarker.Array: + jObject[key] = Deserialize(serializedBytes); + break; + case TypeMarker.Object: + jObject[key] = Deserialize(serializedBytes); + break; + default: + Debug.Fail(string.Format("Unexpected type marker {0}", typeMarker)); + break; + } + + T Deserialize(ReadOnlySpan serializedBytes) + where T : JToken + { + char[] buffer = arrayPoolManager.Rent(SqlVarCharSerializer.GetDeserializedMaxLength(serializedBytes.Length)); + int length = SqlVarCharSerializer.Deserialize(serializedBytes, buffer.AsSpan()); + + JsonSerializer serializer = JsonSerializer.Create(JsonSerializerSettings); + + using MemoryTextReader memoryTextReader = new (new Memory(buffer, 0, length)); + using JsonTextReader reader = new (memoryTextReader); + + return serializer.Deserialize(reader); + } + } +#pragma warning restore SA1101 // Prefix local calls with this + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/MdeEncryptionProcessor.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/MdeEncryptionProcessor.cs new file mode 100644 index 0000000000..2de713774e --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/MdeEncryptionProcessor.cs @@ -0,0 +1,188 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation +{ + using System; + using System.IO; + using System.Threading; + using System.Threading.Tasks; + using Newtonsoft.Json.Linq; + + internal class MdeEncryptionProcessor + { + internal MdeJObjectEncryptionProcessor JObjectEncryptionProcessor { get; set; } = new MdeJObjectEncryptionProcessor(); + +#if NET8_0_OR_GREATER + internal StreamProcessor StreamProcessor { get; set; } = new StreamProcessor(); +#endif + + private readonly Lazy newtonsoftAdapter; +#if NET8_0_OR_GREATER + private readonly Lazy streamAdapter; +#endif + + public MdeEncryptionProcessor() + { + this.newtonsoftAdapter = new Lazy(() => new NewtonsoftAdapter(this.JObjectEncryptionProcessor)); +#if NET8_0_OR_GREATER + this.streamAdapter = new Lazy(() => new SystemTextJsonStreamAdapter(this.StreamProcessor)); +#endif + } + + public async Task EncryptAsync( + Stream input, + Encryptor encryptor, + EncryptionOptions encryptionOptions, + JsonProcessor jsonProcessor, + CosmosDiagnosticsContext diagnosticsContext, + CancellationToken token) + { + ArgumentValidation.ThrowIfNull(diagnosticsContext); + + using IDisposable selectionScope = diagnosticsContext.CreateScope(CosmosDiagnosticsContext.ScopeEncryptModeSelectionPrefix + jsonProcessor); + + IMdeJsonProcessorAdapter adapter = this.GetAdapter(jsonProcessor); + return await adapter.EncryptAsync(input, encryptor, encryptionOptions, token); + } + + internal async Task DecryptObjectAsync( + JObject document, + Encryptor encryptor, + EncryptionProperties encryptionProperties, + CosmosDiagnosticsContext diagnosticsContext, + CancellationToken cancellationToken) + { + return await this.JObjectEncryptionProcessor.DecryptObjectAsync(document, encryptor, encryptionProperties, diagnosticsContext, cancellationToken); + } + + public async Task<(Stream, DecryptionContext)> DecryptAsync( + Stream input, + Encryptor encryptor, + CosmosDiagnosticsContext diagnosticsContext, + RequestOptions requestOptions, + CancellationToken cancellationToken) + { + if (input == null) + { + return (input, null); + } + + ArgumentValidation.ThrowIfNull(diagnosticsContext); + + JsonProcessor jsonProcessor = this.GetRequestedJsonProcessor(requestOptions); + using IDisposable selectionScope = diagnosticsContext.CreateScope(CosmosDiagnosticsContext.ScopeDecryptModeSelectionPrefix + jsonProcessor); + + IMdeJsonProcessorAdapter adapter = this.GetAdapter(jsonProcessor); + return await adapter.DecryptAsync(input, encryptor, diagnosticsContext, cancellationToken); + } + + public async Task DecryptAsync( + Stream input, + Stream output, + Encryptor encryptor, + CosmosDiagnosticsContext diagnosticsContext, + RequestOptions requestOptions, + CancellationToken cancellationToken) + { + if (input == null) + { + return null; + } + + ArgumentValidation.ThrowIfNull(diagnosticsContext); + + JsonProcessor jsonProcessor = this.GetRequestedJsonProcessor(requestOptions); +#if NET8_0_OR_GREATER + using IDisposable selectionScope = diagnosticsContext.CreateScope(CosmosDiagnosticsContext.ScopeDecryptModeSelectionPrefix + jsonProcessor); +#endif + + IMdeJsonProcessorAdapter adapter = this.GetAdapter(jsonProcessor); + return await adapter.DecryptAsync(input, output, encryptor, diagnosticsContext, cancellationToken); + } + + public async Task EncryptAsync( + Stream input, + Stream output, + Encryptor encryptor, + EncryptionOptions encryptionOptions, + JsonProcessor jsonProcessor, + CosmosDiagnosticsContext diagnosticsContext, + CancellationToken cancellationToken) + { + ArgumentValidation.ThrowIfNull(diagnosticsContext); + +#if NET8_0_OR_GREATER + if (jsonProcessor == JsonProcessor.Stream) + { + using IDisposable selectionScope = diagnosticsContext.CreateScope(CosmosDiagnosticsContext.ScopeEncryptModeSelectionPrefix + JsonProcessor.Stream); + IMdeJsonProcessorAdapter adapter = this.GetAdapter(JsonProcessor.Stream); + await adapter.EncryptAsync(input, output, encryptor, encryptionOptions, jsonProcessor, cancellationToken); + return; + } +#endif + + // Fall back to Newtonsoft for netstandard2.0 or when Stream processor not requested + IMdeJsonProcessorAdapter newtonsoftAdapter = this.GetAdapter(JsonProcessor.Newtonsoft); + Stream encryptedStream = await newtonsoftAdapter.EncryptAsync(input, encryptor, encryptionOptions, cancellationToken); + await encryptedStream.CopyToAsync(output); + await encryptedStream.DisposeCompatAsync(); + } + +#if NET8_0_OR_GREATER + public async Task<(Stream, DecryptionContext)> DecryptStreamAsync( + Stream input, + Encryptor encryptor, + EncryptionProperties properties, + CosmosDiagnosticsContext diagnosticsContext, + CancellationToken cancellationToken) + { + MemoryStream ms = new (); + DecryptionContext context = await this.StreamProcessor.DecryptStreamAsync(input, ms, encryptor, properties, diagnosticsContext, cancellationToken); + if (context == null) + { + return (input, null); + } + + return (ms, context); + } + + public async Task DecryptStreamAsync( + Stream input, + Stream output, + Encryptor encryptor, + EncryptionProperties properties, + CosmosDiagnosticsContext diagnosticsContext, + CancellationToken cancellationToken) + { + return await this.StreamProcessor.DecryptStreamAsync(input, output, encryptor, properties, diagnosticsContext, cancellationToken); + } +#endif + + private JsonProcessor GetRequestedJsonProcessor(RequestOptions requestOptions) + { +#if NET8_0_OR_GREATER + if (requestOptions != null && requestOptions.TryReadJsonProcessorOverride(out JsonProcessor overrideProcessor)) + { + return overrideProcessor; + } +#endif + + return JsonProcessor.Newtonsoft; + } + + private IMdeJsonProcessorAdapter GetAdapter(JsonProcessor jsonProcessor) + { + return jsonProcessor switch + { + JsonProcessor.Newtonsoft => this.newtonsoftAdapter.Value, +#if NET8_0_OR_GREATER + JsonProcessor.Stream => this.streamAdapter.Value, +#endif + _ => throw new NotSupportedException($"JsonProcessor '{jsonProcessor}' is not supported on this platform. " + + $"Supported processors: {string.Join(", ", Enum.GetNames(typeof(JsonProcessor)))}") + }; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/MdeJObjectEncryptionProcessor.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/MdeJObjectEncryptionProcessor.cs new file mode 100644 index 0000000000..8d52bfc992 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/MdeJObjectEncryptionProcessor.cs @@ -0,0 +1,146 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Newtonsoft.Json.Linq; + + internal class MdeJObjectEncryptionProcessor + { + internal JObjectSqlSerializer Serializer { get; set; } = new JObjectSqlSerializer(); + + internal MdeEncryptor Encryptor { get; set; } = new MdeEncryptor(); + + public async Task EncryptAsync( + Stream input, + Encryptor encryptor, + EncryptionOptions encryptionOptions, + CancellationToken token) + { + JObject itemJObj = EncryptionProcessor.BaseSerializer.FromStream(input); + + Stream result = await this.EncryptAsync(itemJObj, encryptor, encryptionOptions, token); + + await input.DisposeCompatAsync(); + + return result; + } + + public async Task EncryptAsync( + JObject input, + Encryptor encryptor, + EncryptionOptions encryptionOptions, + CancellationToken token) + { + List pathsEncrypted = new (); + TypeMarker typeMarker; + + using ArrayPoolManager arrayPoolManager = new (); + + DataEncryptionKey encryptionKey = await encryptor.GetEncryptionKeyAsync(encryptionOptions.DataEncryptionKeyId, encryptionOptions.EncryptionAlgorithm, token); + + foreach (string pathToEncrypt in encryptionOptions.PathsToEncrypt) + { + string propertyName = pathToEncrypt.Substring(1); + if (!input.TryGetValue(propertyName, out JToken propertyValue)) + { + continue; + } + + if (propertyValue.Type == JTokenType.Null) + { + continue; + } + + byte[] processedBytes = null; + (typeMarker, processedBytes, int processedBytesLength) = this.Serializer.Serialize(propertyValue, arrayPoolManager); + + if (processedBytes == null) + { + continue; + } + + byte[] encryptedBytes = this.Encryptor.Encrypt(encryptionKey, typeMarker, processedBytes, processedBytesLength); + + input[propertyName] = encryptedBytes; + + pathsEncrypted.Add(pathToEncrypt); + } + + EncryptionProperties encryptionProperties = new ( + encryptionFormatVersion: EncryptionFormatVersion.Mde, + encryptionOptions.EncryptionAlgorithm, + encryptionOptions.DataEncryptionKeyId, + encryptedData: null, + pathsEncrypted); + + input.Add(Constants.EncryptedInfo, JObject.FromObject(encryptionProperties)); + + return EncryptionProcessor.BaseSerializer.ToStream(input); + } + + internal async Task DecryptObjectAsync( + JObject document, + Encryptor encryptor, + EncryptionProperties encryptionProperties, + CosmosDiagnosticsContext diagnosticsContext, + CancellationToken cancellationToken) + { + _ = diagnosticsContext; + + if (encryptionProperties.EncryptionFormatVersion != EncryptionFormatVersion.Mde) + { + throw new NotSupportedException($"Unknown encryption format version: {encryptionProperties.EncryptionFormatVersion}. Please upgrade your SDK to the latest version."); + } + + using ArrayPoolManager arrayPoolManager = new (); + using ArrayPoolManager charPoolManager = new (); + + DataEncryptionKey encryptionKey = await encryptor.GetEncryptionKeyAsync(encryptionProperties.DataEncryptionKeyId, encryptionProperties.EncryptionAlgorithm, cancellationToken); + + List pathsDecrypted = new (encryptionProperties.EncryptedPaths.Count()); + + foreach (string path in encryptionProperties.EncryptedPaths) + { + string propertyName = path.Substring(1); + + if (!document.TryGetValue(propertyName, out JToken propertyValue)) + { + // malformed document, such record shouldn't be there at all + continue; + } + + byte[] cipherTextWithTypeMarker = propertyValue.ToObject(); + if (cipherTextWithTypeMarker == null) + { + continue; + } + + (byte[] bytes, int processedBytes) = this.Encryptor.Decrypt(encryptionKey, cipherTextWithTypeMarker, cipherTextWithTypeMarker.Length, arrayPoolManager); + + this.Serializer.DeserializeAndAddProperty( + (TypeMarker)cipherTextWithTypeMarker[0], + bytes.AsSpan(0, processedBytes), + document, + propertyName, + charPoolManager); + + pathsDecrypted.Add(path); + } + + DecryptionContext decryptionContext = EncryptionProcessor.CreateDecryptionContext( + pathsDecrypted, + encryptionProperties.DataEncryptionKeyId); + + document.Remove(Constants.EncryptedInfo); + return decryptionContext; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/MemoryTextReader.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/MemoryTextReader.cs index c3f96c274a..a34185c5e3 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/MemoryTextReader.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/MemoryTextReader.cs @@ -71,32 +71,10 @@ public override int Read() public override int Read(char[] buffer, int index, int count) { -#if NET8_0_OR_GREATER - ArgumentNullException.ThrowIfNull(buffer); - ArgumentOutOfRangeException.ThrowIfNegative(index); - ArgumentOutOfRangeException.ThrowIfNegative(count); - ArgumentOutOfRangeException.ThrowIfGreaterThan(count, buffer.Length - index); -#else - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (index < 0) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - if (buffer.Length - index < count) - { - throw new ArgumentOutOfRangeException(); - } -#endif + ArgumentValidation.ThrowIfNull(buffer); + ArgumentValidation.ThrowIfNegative(index); + ArgumentValidation.ThrowIfNegative(count); + ArgumentValidation.ThrowIfGreaterThan(count, buffer.Length - index); if (this.closed) { @@ -126,11 +104,8 @@ public override string ReadToEnd() } this.pos = this.length; -#if NET8_0_OR_GREATER - return new string(this.chars[this.pos..this.length].Span); -#else - return new string(this.chars.Slice(this.pos, this.length - this.pos).ToArray()); -#endif + Memory remaining = this.chars.Slice(this.pos, this.length - this.pos); + return new string(remaining.ToArray()); } public override string ReadLine() @@ -146,11 +121,7 @@ public override string ReadLine() char ch = this.chars.Span[i]; if (ch == '\r' || ch == '\n') { -#if NET8_0_OR_GREATER - string result = new (this.chars[this.pos..i].Span); -#else string result = new (this.chars.Slice(this.pos, i - this.pos).ToArray()); -#endif this.pos = i + 1; if (ch == '\r' && this.pos < this.length && this.chars.Span[this.pos] == '\n') { @@ -165,11 +136,7 @@ public override string ReadLine() if (i > this.pos) { -#if NET8_0_OR_GREATER - string result = new (this.chars[this.pos..i].Span); -#else string result = new (this.chars.Slice(this.pos, i - this.pos).ToArray()); -#endif this.pos = i; return result; } diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/NewtonsoftAdapter.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/NewtonsoftAdapter.cs new file mode 100644 index 0000000000..87a41d610d --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/NewtonsoftAdapter.cs @@ -0,0 +1,139 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation; + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; + +internal sealed class NewtonsoftAdapter : IMdeJsonProcessorAdapter +{ + private enum MdePropertyStatus + { + None, + Mde, + LegacyOther, + } + + private readonly MdeJObjectEncryptionProcessor jObjectProcessor; + + public NewtonsoftAdapter(MdeJObjectEncryptionProcessor jObjectProcessor) + { + this.jObjectProcessor = jObjectProcessor; + } + + public Task EncryptAsync(Stream input, Encryptor encryptor, EncryptionOptions options, CancellationToken cancellationToken) + { + return this.jObjectProcessor.EncryptAsync(input, encryptor, options, cancellationToken); + } + + public Task EncryptAsync(Stream input, Stream output, Encryptor encryptor, EncryptionOptions options, JsonProcessor jsonProcessor, CancellationToken cancellationToken) + { + throw new NotSupportedException("This overload is only supported for Stream JsonProcessor"); + } + + public async Task<(Stream, DecryptionContext)> DecryptAsync(Stream input, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) + { + (MdePropertyStatus status, JObject itemJObj, EncryptionProperties encryptionProperties) = this.InspectForMde(input); + switch (status) + { + case MdePropertyStatus.Mde: + { + DecryptionContext context = await this.jObjectProcessor.DecryptObjectAsync(itemJObj, encryptor, encryptionProperties, diagnosticsContext, cancellationToken); + await input.DisposeCompatAsync(); + + MemoryStream direct = new (capacity: 1024); + EncryptionProcessor.BaseSerializer.WriteToStream(itemJObj, direct); + direct.Position = 0; // Reset position for caller to read from beginning + return (direct, context); + } + + case MdePropertyStatus.None: + case MdePropertyStatus.LegacyOther: + default: + return (input, null); + } + } + + public async Task DecryptAsync(Stream input, Stream output, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) + { + (MdePropertyStatus status, JObject itemJObj, EncryptionProperties encryptionProperties) = this.InspectForMde(input); + switch (status) + { + case MdePropertyStatus.Mde: + { + DecryptionContext context = await this.jObjectProcessor.DecryptObjectAsync(itemJObj, encryptor, encryptionProperties, diagnosticsContext, cancellationToken); + EncryptionProcessor.BaseSerializer.WriteToStream(itemJObj, output); + output.Position = 0; // Reset position for caller to read from beginning + await input.DisposeCompatAsync(); + return context; + } + + case MdePropertyStatus.None: + if (input.CanSeek) + { + input.Position = 0; + } + + return null; + case MdePropertyStatus.LegacyOther: + default: + return null; + } + } + + private (MdePropertyStatus status, JObject itemJObj, EncryptionProperties encryptionProperties) InspectForMde(Stream input) + { + input.Position = 0; + JObject itemJObj = this.ReadJObject(input); + JObject encryptionProperties = this.RetrieveEncryptionProperties(itemJObj); + if (encryptionProperties == null) + { + input.Position = 0; + return (MdePropertyStatus.None, null, null); + } + + EncryptionProperties parsed = encryptionProperties.ToObject(); +#pragma warning disable CS0618 + if (parsed.EncryptionAlgorithm != CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized) + { + input.Position = 0; + return (MdePropertyStatus.LegacyOther, null, null); + } +#pragma warning restore CS0618 + + return (MdePropertyStatus.Mde, itemJObj, parsed); + } + + private JObject ReadJObject(Stream input) + { + using StreamReader sr = new (input, System.Text.Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true); + using Newtonsoft.Json.JsonTextReader jsonTextReader = new (sr) + { + ArrayPool = JsonArrayPool.Instance, + }; + + Newtonsoft.Json.JsonSerializerSettings settings = new () + { + DateParseHandling = Newtonsoft.Json.DateParseHandling.None, + MaxDepth = 64, + }; + + return Newtonsoft.Json.JsonSerializer.Create(settings).Deserialize(jsonTextReader); + } + + private JObject RetrieveEncryptionProperties(JObject item) + { + JProperty encryptionPropertiesJProp = item.Property(Constants.EncryptedInfo); + if (encryptionPropertiesJProp?.Value is JObject jObject) + { + return jObject; + } + + return null; + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Decryptor.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Decryptor.cs index d4ca1ccfdf..650bd9181e 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Decryptor.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Decryptor.cs @@ -2,7 +2,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // ------------------------------------------------------------ -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +#if NET8_0_OR_GREATER namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation { using System; @@ -39,18 +39,11 @@ internal async Task DecryptStreamAsync( { _ = diagnosticsContext; - if (properties.EncryptionFormatVersion != EncryptionFormatVersion.Mde && properties.EncryptionFormatVersion != EncryptionFormatVersion.MdeWithCompression) + if (properties.EncryptionFormatVersion != EncryptionFormatVersion.Mde) { throw new NotSupportedException($"Unknown encryption format version: {properties.EncryptionFormatVersion}. Please upgrade your SDK to the latest version."); } - bool containsCompressed = properties.CompressedEncryptedPaths?.Count > 0; - - if (properties.CompressionAlgorithm != CompressionOptions.CompressionAlgorithm.Brotli && containsCompressed) - { - throw new NotSupportedException($"Unknown compression algorithm {properties.CompressionAlgorithm}"); - } - using ArrayPoolManager arrayPoolManager = new (); DataEncryptionKey encryptionKey = await encryptor.GetEncryptionKeyAsync(properties.DataEncryptionKeyId, properties.EncryptionAlgorithm, cancellationToken); @@ -140,7 +133,7 @@ long TransformDecryptBuffer(ReadOnlySpan buffer) decryptPropertyName = null; writer.WriteRawValue(reader.ValueSpan); break; - case JsonTokenType.None: + case JsonTokenType.None: // Unreachable: pre-first-Read state decryptPropertyName = null; break; case JsonTokenType.StartObject: @@ -177,7 +170,7 @@ long TransformDecryptBuffer(ReadOnlySpan buffer) writer.WritePropertyName(reader.ValueSpan); break; - case JsonTokenType.Comment: + case JsonTokenType.Comment: // Skipped via reader options break; case JsonTokenType.True: decryptPropertyName = null; @@ -213,15 +206,6 @@ void TransformDecryptProperty(ref Utf8JsonReader reader) (byte[] bytes, int processedBytes) = this.Encryptor.Decrypt(encryptionKey, cipherTextWithTypeMarker, cipherTextLength, arrayPoolManager); - if (containsCompressed && properties.CompressedEncryptedPaths?.TryGetValue(decryptPropertyName, out int decompressedSize) == true) - { - BrotliCompressor decompressor = new (); - byte[] buffer = arrayPoolManager.Rent(decompressedSize); - processedBytes = decompressor.Decompress(bytes, processedBytes, buffer); - - bytes = buffer; - } - ReadOnlySpan bytesToWrite = bytes.AsSpan(0, processedBytes); switch ((TypeMarker)cipherTextWithTypeMarker[0]) { @@ -237,7 +221,7 @@ void TransformDecryptProperty(ref Utf8JsonReader reader) case TypeMarker.Boolean: writer.WriteBooleanValue(SqlBoolSerializer.Deserialize(bytesToWrite)); break; - case TypeMarker.Null: + case TypeMarker.Null: // Produced only if ciphertext was forged or future versions choose to encrypt nulls; current encryptor skips nulls. writer.WriteNullValue(); break; default: diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs index a3980ae56a..11988f4e2a 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs @@ -2,7 +2,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // ------------------------------------------------------------ -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +#if NET8_0_OR_GREATER namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation { using System; @@ -30,15 +30,8 @@ internal async Task EncryptStreamAsync( DataEncryptionKey encryptionKey = await encryptor.GetEncryptionKeyAsync(encryptionOptions.DataEncryptionKeyId, encryptionOptions.EncryptionAlgorithm, cancellationToken); - bool compressionEnabled = encryptionOptions.CompressionOptions.Algorithm != CompressionOptions.CompressionAlgorithm.None; - - BrotliCompressor compressor = encryptionOptions.CompressionOptions.Algorithm == CompressionOptions.CompressionAlgorithm.Brotli - ? new BrotliCompressor(encryptionOptions.CompressionOptions.CompressionLevel) : null; - HashSet pathsToEncrypt = encryptionOptions.PathsToEncrypt as HashSet ?? new (encryptionOptions.PathsToEncrypt, StringComparer.Ordinal); - Dictionary compressedPaths = new (); - using Utf8JsonWriter writer = new (outputStream); byte[] buffer = arrayPoolManager.Rent(InitialBufferSize); @@ -52,19 +45,18 @@ internal async Task EncryptStreamAsync( Utf8JsonWriter encryptionPayloadWriter = null; string encryptPropertyName = null; RentArrayBufferWriter bufferWriter = null; + bool firstTokenValidated = false; while (!isFinalBlock) { int dataLength = await inputStream.ReadAsync(buffer.AsMemory(leftOver, buffer.Length - leftOver), cancellationToken); int dataSize = dataLength + leftOver; isFinalBlock = dataSize == 0; - long bytesConsumed = 0; - bytesConsumed = TransformEncryptBuffer(buffer.AsSpan(0, dataSize)); + long bytesConsumed = TransformEncryptBuffer(buffer.AsSpan(0, dataSize)); leftOver = dataSize - (int)bytesConsumed; - // we need to scale out buffer if (leftOver == dataSize) { byte[] newBuffer = arrayPoolManager.Rent(buffer.Length * 2); @@ -80,13 +72,11 @@ internal async Task EncryptStreamAsync( await inputStream.DisposeAsync(); EncryptionProperties encryptionProperties = new ( - encryptionFormatVersion: compressionEnabled ? 4 : 3, + encryptionFormatVersion: EncryptionFormatVersion.Mde, encryptionOptions.EncryptionAlgorithm, encryptionOptions.DataEncryptionKeyId, encryptedData: null, - pathsEncrypted, - encryptionOptions.CompressionOptions.Algorithm, - compressedPaths); + pathsEncrypted); writer.WritePropertyName(this.encryptionPropertiesNameBytes); JsonSerializer.Serialize(writer, encryptionProperties); @@ -101,13 +91,30 @@ long TransformEncryptBuffer(ReadOnlySpan buffer) while (reader.Read()) { - Utf8JsonWriter currentWriter = encryptionPayloadWriter ?? writer; - JsonTokenType tokenType = reader.TokenType; + if (!firstTokenValidated) + { + // The first non-None token must be StartObject for streaming encryption. + if (tokenType == JsonTokenType.StartObject) + { + firstTokenValidated = true; + } + else if (tokenType == JsonTokenType.Comment || tokenType == JsonTokenType.None) + { + continue; // skip and keep waiting for first structural token + } + else + { + throw new NotSupportedException("Streaming encryption requires a JSON object root. Root arrays or primitive values are not supported."); + } + } + + Utf8JsonWriter currentWriter = encryptionPayloadWriter ?? writer; + switch (tokenType) { - case JsonTokenType.None: + case JsonTokenType.None: // Unreachable after first Read() break; case JsonTokenType.StartObject: if (encryptPropertyName != null && encryptionPayloadWriter == null) @@ -187,7 +194,7 @@ long TransformEncryptBuffer(ReadOnlySpan buffer) currentWriter.WritePropertyName(reader.ValueSpan); break; - case JsonTokenType.Comment: + case JsonTokenType.Comment: // Skipped via reader options currentWriter.WriteCommentValue(reader.ValueSpan); break; case JsonTokenType.String: @@ -249,6 +256,7 @@ long TransformEncryptBuffer(ReadOnlySpan buffer) break; case JsonTokenType.Null: currentWriter.WriteNullValue(); + encryptPropertyName = null; break; } } @@ -262,13 +270,6 @@ ReadOnlySpan TransformEncryptPayload(byte[] payload, int payloadSize, Type byte[] processedBytes = payload; int processedBytesLength = payloadSize; - if (compressor != null && payloadSize >= encryptionOptions.CompressionOptions.MinimalCompressedLength) - { - byte[] compressedBytes = arrayPoolManager.Rent(BrotliCompressor.GetMaxCompressedSize(payloadSize)); - processedBytesLength = compressor.Compress(compressedPaths, encryptPropertyName, processedBytes, payloadSize, compressedBytes); - processedBytes = compressedBytes; - } - (byte[] encryptedBytes, int encryptedBytesCount) = this.Encryptor.Encrypt(encryptionKey, typeMarker, processedBytes, processedBytesLength, arrayPoolManager); pathsEncrypted.Add(encryptPropertyName); @@ -287,18 +288,21 @@ private static (byte[] buffer, int length) Serialize(bool value, ArrayPoolManage private static (TypeMarker typeMarker, byte[] buffer, int length) SerializeNumber(ReadOnlySpan utf8bytes, ArrayPoolManager arrayPoolManager) { - if (long.TryParse(utf8bytes, out long longValue)) + if (System.Buffers.Text.Utf8Parser.TryParse(utf8bytes, out long longValue, out int consumedLong) && consumedLong == utf8bytes.Length) { return Serialize(longValue, arrayPoolManager); } - else if (double.TryParse(utf8bytes, out double doubleValue)) - { - return Serialize(doubleValue, arrayPoolManager); - } - else + + if (System.Buffers.Text.Utf8Parser.TryParse(utf8bytes, out double doubleValue, out int consumedDouble) && consumedDouble == utf8bytes.Length) { - throw new InvalidOperationException("Unsupported Number type"); + // Reject non-finite numbers to keep JSON contract compatibility + if (double.IsFinite(doubleValue)) + { + return Serialize(doubleValue, arrayPoolManager); + } } + + throw new InvalidOperationException("Unsupported Number type"); } private static (TypeMarker typeMarker, byte[] buffer, int length) Serialize(long value, ArrayPoolManager arrayPoolManager) diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/SystemTextJsonStreamAdapter.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/SystemTextJsonStreamAdapter.cs new file mode 100644 index 0000000000..9da7361335 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/SystemTextJsonStreamAdapter.cs @@ -0,0 +1,111 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation; + +using System; +using System.IO; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +#if NET8_0_OR_GREATER +internal sealed class SystemTextJsonStreamAdapter : IMdeJsonProcessorAdapter +{ + private readonly StreamProcessor streamProcessor; + + public SystemTextJsonStreamAdapter(StreamProcessor streamProcessor) + { + this.streamProcessor = streamProcessor; + } + + public async Task EncryptAsync(Stream input, Encryptor encryptor, EncryptionOptions options, CancellationToken cancellationToken) + { + MemoryStream ms = new (); + await this.streamProcessor.EncryptStreamAsync(input, ms, encryptor, options, cancellationToken); + return ms; + } + + public Task EncryptAsync(Stream input, Stream output, Encryptor encryptor, EncryptionOptions options, JsonProcessor jsonProcessor, CancellationToken cancellationToken) + { + if (jsonProcessor != JsonProcessor.Stream) + { + throw new NotSupportedException("This overload is only supported for Stream JsonProcessor"); + } + + return this.streamProcessor.EncryptStreamAsync(input, output, encryptor, options, cancellationToken); + } + + public async Task<(Stream, DecryptionContext)> DecryptAsync(Stream input, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) + { + EncryptionProperties properties = await this.ReadMdeEncryptionPropertiesStreamingAsync(input, cancellationToken); + if (properties == null) + { + return (input, null); + } + + MemoryStream ms = new (); + DecryptionContext context = await this.streamProcessor.DecryptStreamAsync(input, ms, encryptor, properties, diagnosticsContext, cancellationToken); + if (context == null) + { + return (input, null); + } + + return (ms, context); + } + + public async Task DecryptAsync(Stream input, Stream output, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) + { + EncryptionProperties properties = await this.ReadMdeEncryptionPropertiesStreamingAsync(input, cancellationToken); + if (properties == null) + { + if (input.CanSeek) + { + input.Position = 0; + } + + return null; + } + + DecryptionContext context = await this.streamProcessor.DecryptStreamAsync(input, output, encryptor, properties, diagnosticsContext, cancellationToken); + if (context == null) + { + if (input.CanSeek) + { + input.Position = 0; + } + + return null; + } + + await input.DisposeAsync(); + return context; + } + + /// + /// Reads encryption properties from the stream using System.Text.Json streaming API. + /// Returns null if no encryption properties are found. + /// Throws NotSupportedException if legacy encryption algorithm is detected. + /// + private async Task ReadMdeEncryptionPropertiesStreamingAsync(Stream input, CancellationToken cancellationToken) + { + input.Position = 0; + EncryptionPropertiesWrapper properties = await JsonSerializer.DeserializeAsync(input, cancellationToken: cancellationToken); + input.Position = 0; + if (properties?.EncryptionProperties == null) + { + return null; + } + +#pragma warning disable CS0618 + if (properties.EncryptionProperties.EncryptionAlgorithm != CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized) + { + throw new NotSupportedException($"JsonProcessor.Stream is not supported for encryption algorithm '{properties.EncryptionProperties.EncryptionAlgorithm}'. Only '{CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized}' is supported with the Stream processor."); + } +#pragma warning restore CS0618 + + return properties.EncryptionProperties; + } +} +#endif \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Utils/ArgumentValidation.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Utils/ArgumentValidation.cs new file mode 100644 index 0000000000..e15bf3b2fd --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Utils/ArgumentValidation.cs @@ -0,0 +1,176 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom +{ + using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; + + /// + /// Provides backwards-compatible argument validation methods optimized for performance. + /// + internal static class ArgumentValidation + { + /// + /// Throws an if is null. + /// + /// The reference type argument to validate as non-null. + /// The name of the parameter with which corresponds. + /// is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DebuggerStepThrough] + public static void ThrowIfNull( +#if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNull] +#endif + object argument, +#if NET6_0_OR_GREATER + [CallerArgumentExpression(nameof(argument))] string paramName = null) +#else + string paramName = null) +#endif + { +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNull(argument, paramName); +#else + if (argument is null) + { + throw new ArgumentNullException(paramName); + } +#endif + } + + /// + /// Throws an if is null, + /// or an if it is empty. + /// + /// The string argument to validate as non-null and non-empty. + /// The name of the parameter with which corresponds. + /// is null. + /// is empty. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DebuggerStepThrough] + public static void ThrowIfNullOrEmpty( +#if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNull] +#endif + string argument, +#if NET6_0_OR_GREATER + [CallerArgumentExpression(nameof(argument))] string paramName = null) +#else + string paramName = null) +#endif + { +#if NET8_0_OR_GREATER + ArgumentException.ThrowIfNullOrEmpty(argument, paramName); +#else + if (argument is null) + { + throw new ArgumentNullException(paramName); + } + + if (argument.Length == 0) + { + throw new ArgumentException("The value cannot be an empty string.", paramName); + } +#endif + } + + /// + /// Throws an if is null, + /// or an if it is empty or contains only white-space characters. + /// + /// The string argument to validate. + /// The name of the parameter with which corresponds. + /// is null. + /// is empty or whitespace. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DebuggerStepThrough] + public static void ThrowIfNullOrWhiteSpace( +#if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.NotNull] +#endif + string argument, +#if NET6_0_OR_GREATER + [CallerArgumentExpression(nameof(argument))] string paramName = null) +#else + string paramName = null) +#endif + { +#if NET8_0_OR_GREATER + ArgumentException.ThrowIfNullOrWhiteSpace(argument, paramName); +#else + if (argument is null) + { + throw new ArgumentNullException(paramName); + } + + if (argument.Length == 0) + { + throw new ArgumentException("The value cannot be an empty string.", paramName); + } + + if (string.IsNullOrWhiteSpace(argument)) + { + throw new ArgumentException("The value cannot be an empty string or composed entirely of whitespace.", paramName); + } +#endif + } + + /// + /// Throws an if is negative. + /// + /// The argument to validate as non-negative. + /// The name of the parameter with which corresponds. + /// is negative. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DebuggerStepThrough] + public static void ThrowIfNegative( + int value, +#if NET6_0_OR_GREATER + [CallerArgumentExpression(nameof(value))] string paramName = null) +#else + string paramName = null) +#endif + { +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfNegative(value, paramName); +#else + if (value < 0) + { + throw new ArgumentOutOfRangeException(paramName, value, $"'{paramName}' must be a non-negative value."); + } +#endif + } + + /// + /// Throws an if is greater than . + /// + /// The argument to validate. + /// The value to compare with . + /// The name of the parameter with which corresponds. + /// is greater than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DebuggerStepThrough] + public static void ThrowIfGreaterThan( + int value, + int other, +#if NET6_0_OR_GREATER + [CallerArgumentExpression(nameof(value))] string paramName = null) +#else + string paramName = null) +#endif + { +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfGreaterThan(value, other, paramName); +#else + if (value > other) + { + throw new ArgumentOutOfRangeException(paramName, value, $"'{paramName}' must be less than or equal to {other}."); + } +#endif + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/EmulatorTests/MdeCustomEncryptionTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/EmulatorTests/MdeCustomEncryptionTests.cs index 14b1915abb..37171e41fc 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/EmulatorTests/MdeCustomEncryptionTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/EmulatorTests/MdeCustomEncryptionTests.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------ +//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ @@ -6,6 +6,7 @@ namespace Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests { using System; using System.Collections.Generic; + using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -273,6 +274,429 @@ await IterateDekFeedAsync( } } +#if NET8_0_OR_GREATER + [TestMethod] + public async Task BackCompat_RoundTrip_WithAndWithoutJsonProcessorOverride() + { + // Arrange: simple POCO with one encrypted path + TestItem testItem = new TestItem + { + Id = Guid.NewGuid().ToString(), + PK = Guid.NewGuid().ToString(), + NonSensitive = "hello", + Sensitive = "secret-value" + }; + + ItemRequestOptions baselineRequestOptions = null; // no override + ItemRequestOptions overrideRequestOptions = new() + { + Properties = new Dictionary + { + { "encryption-json-processor", "Stream" } + } + }; + + Container container = encryptionContainer; // already wrapped with encryptor + + // Act: create with baseline + ItemResponse baselineCreate = await container.CreateItemAsync( + testItem, + new PartitionKey(testItem.PK), + baselineRequestOptions, + cancellationToken: CancellationToken.None); + Assert.AreEqual(HttpStatusCode.Created, baselineCreate.StatusCode); + + // Read back with no override + ItemResponse baselineRead = await container.ReadItemAsync( + testItem.Id, + new PartitionKey(testItem.PK), + baselineRequestOptions, + cancellationToken: CancellationToken.None); + + // Read back with stream override (should still decrypt identically) + ItemResponse overrideRead = await container.ReadItemAsync( + testItem.Id, + new PartitionKey(testItem.PK), + overrideRequestOptions, + cancellationToken: CancellationToken.None); + + // Assert decrypted values identical + Assert.AreEqual(testItem.NonSensitive, baselineRead.Resource.NonSensitive); + Assert.AreEqual(testItem.Sensitive, baselineRead.Resource.Sensitive); + Assert.AreEqual(testItem.NonSensitive, overrideRead.Resource.NonSensitive); + Assert.AreEqual(testItem.Sensitive, overrideRead.Resource.Sensitive); + + // Replace item using stream override and confirm baseline path still works + testItem.NonSensitive = "hello-updated"; + ItemResponse replaceWithOverride = await container.ReplaceItemAsync( + testItem, + testItem.Id, + new PartitionKey(testItem.PK), + overrideRequestOptions, + cancellationToken: CancellationToken.None); + Assert.AreEqual(HttpStatusCode.OK, replaceWithOverride.StatusCode); + + ItemResponse finalReadBaseline = await container.ReadItemAsync( + testItem.Id, + new PartitionKey(testItem.PK), + baselineRequestOptions, + cancellationToken: CancellationToken.None); + + Assert.AreEqual(testItem.NonSensitive, finalReadBaseline.Resource.NonSensitive); + Assert.AreEqual(testItem.Sensitive, finalReadBaseline.Resource.Sensitive); + } + + [TestMethod] + public async Task StreamProcessor_EndToEnd_RoundTrip() + { + TestItem testItem = new TestItem + { + Id = Guid.NewGuid().ToString(), + PK = Guid.NewGuid().ToString(), + NonSensitive = "plain", + Sensitive = "stream-secret" + }; + + EncryptionItemRequestOptions options = new() + { + EncryptionOptions = new EncryptionOptions + { + DataEncryptionKeyId = dekProperties.Id, + EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + PathsToEncrypt = new List { "/Sensitive" }, + }, + Properties = new Dictionary + { + { "encryption-json-processor", "Stream" } + } + }; + + ItemResponse create = await encryptionContainer.CreateItemAsync(testItem, new PartitionKey(testItem.PK), options); + Assert.AreEqual(HttpStatusCode.Created, create.StatusCode); + + // Read back with stream override explicit (should decrypt) + ItemResponse read = await encryptionContainer.ReadItemAsync(testItem.Id, new PartitionKey(testItem.PK), new ItemRequestOptions + { + Properties = new Dictionary { { "encryption-json-processor", "Stream" } } + }); + Assert.AreEqual(testItem.Sensitive, read.Resource.Sensitive); + } + + [TestMethod] + public async Task StreamProcessor_ScopesReflectStreamOptIn() + { + List scopes = await CaptureEncryptionScopesAsync(async () => + { + TestItem testItem = new TestItem + { + Id = Guid.NewGuid().ToString(), + PK = Guid.NewGuid().ToString(), + NonSensitive = "diag-plain", + Sensitive = "diag-secret" + }; + + EncryptionItemRequestOptions createOptions = CreateEncryptionItemRequestOptions( + dekProperties.Id, + CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + JsonProcessor.Stream); + + bool created = false; + try + { + ItemResponse createResponse = await encryptionContainer.CreateItemAsync( + testItem, + new PartitionKey(testItem.PK), + createOptions); + Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); + created = true; + + ItemResponse readResponse = await encryptionContainer.ReadItemAsync( + testItem.Id, + new PartitionKey(testItem.PK), + new ItemRequestOptions + { + Properties = new Dictionary + { + { "encryption-json-processor", "Stream" } + } + }); + + Assert.AreEqual(testItem.Sensitive, readResponse.Resource.Sensitive); + } + finally + { + if (created) + { + await encryptionContainer.DeleteItemAsync( + testItem.Id, + new PartitionKey(testItem.PK)); + } + } + }); + + (int streamEncrypt, int streamDecrypt, int newtonsoftEncrypt, int newtonsoftDecrypt) = CountJsonProcessorScopes(scopes); + + Assert.IsTrue( + streamEncrypt >= 1, + $"Expected to capture stream encrypt scope. Scopes: {string.Join(", ", scopes)}"); + Assert.IsTrue( + streamDecrypt >= 1, + $"Expected to capture stream decrypt scope. Scopes: {string.Join(", ", scopes)}"); + Assert.AreEqual( + 0, + newtonsoftEncrypt, + $"Did not expect newtonsoft encrypt scope when opting into stream. Scopes: {string.Join(", ", scopes)}"); + Assert.AreEqual( + 0, + newtonsoftDecrypt, + $"Did not expect newtonsoft decrypt scope when opting into stream. Scopes: {string.Join(", ", scopes)}"); + } + + [TestMethod] + public async Task StreamProcessor_DefaultsToNewtonsoftWhenNoOptIn() + { + List scopes = await CaptureEncryptionScopesAsync(async () => + { + TestItem testItem = new TestItem + { + Id = Guid.NewGuid().ToString(), + PK = Guid.NewGuid().ToString(), + NonSensitive = "default-plain", + Sensitive = "default-secret" + }; + + EncryptionItemRequestOptions createOptions = new() + { + EncryptionOptions = new EncryptionOptions + { + DataEncryptionKeyId = dekProperties.Id, + EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + PathsToEncrypt = new List { "/Sensitive" } + } + }; + + bool created = false; + try + { + ItemResponse createResponse = await encryptionContainer.CreateItemAsync( + testItem, + new PartitionKey(testItem.PK), + createOptions); + Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); + created = true; + + ItemResponse readResponse = await encryptionContainer.ReadItemAsync( + testItem.Id, + new PartitionKey(testItem.PK)); + + Assert.AreEqual(testItem.Sensitive, readResponse.Resource.Sensitive); + } + finally + { + if (created) + { + await encryptionContainer.DeleteItemAsync( + testItem.Id, + new PartitionKey(testItem.PK)); + } + } + }); + + (int streamEncrypt, int streamDecrypt, int newtonsoftEncrypt, int newtonsoftDecrypt) = CountJsonProcessorScopes(scopes); + + Assert.AreEqual( + 0, + streamEncrypt, + $"Did not expect stream encrypt scope without opting in. Scopes: {string.Join(", ", scopes)}"); + Assert.AreEqual( + 0, + streamDecrypt, + $"Did not expect stream decrypt scope without opting in. Scopes: {string.Join(", ", scopes)}"); + Assert.IsTrue( + newtonsoftEncrypt >= 1, + $"Expected to capture newtonsoft encrypt scope. Scopes: {string.Join(", ", scopes)}"); + Assert.IsTrue( + newtonsoftDecrypt >= 1, + $"Expected to capture newtonsoft decrypt scope. Scopes: {string.Join(", ", scopes)}"); + } + + private static async Task> CaptureEncryptionScopesAsync(Func action) + { + List scopes = new List(); + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = source => string.Equals(source.Name, "Microsoft.Azure.Cosmos.Encryption.Custom", StringComparison.Ordinal), + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStopped = activity => + { + if (!string.IsNullOrEmpty(activity?.OperationName)) + { + lock (scopes) + { + scopes.Add(activity.OperationName); + } + } + } + }; + + ActivitySource.AddActivityListener(listener); + try + { + await action(); + } + finally + { + listener.Dispose(); + } + + lock (scopes) + { + return scopes.ToList(); + } + } + + private static (int StreamEncrypt, int StreamDecrypt, int NewtonsoftEncrypt, int NewtonsoftDecrypt) CountJsonProcessorScopes(IEnumerable scopes) + { + string encryptStreamScope = CosmosDiagnosticsContext.ScopeEncryptModeSelectionPrefix + JsonProcessor.Stream; + string decryptStreamScope = CosmosDiagnosticsContext.ScopeDecryptModeSelectionPrefix + JsonProcessor.Stream; + string encryptNewtonsoftScope = CosmosDiagnosticsContext.ScopeEncryptModeSelectionPrefix + JsonProcessor.Newtonsoft; + string decryptNewtonsoftScope = CosmosDiagnosticsContext.ScopeDecryptModeSelectionPrefix + JsonProcessor.Newtonsoft; + + int streamEncrypt = scopes.Count(scope => string.Equals(scope, encryptStreamScope, StringComparison.Ordinal)); + int streamDecrypt = scopes.Count(scope => string.Equals(scope, decryptStreamScope, StringComparison.Ordinal)); + int newtonsoftEncrypt = scopes.Count(scope => string.Equals(scope, encryptNewtonsoftScope, StringComparison.Ordinal)); + int newtonsoftDecrypt = scopes.Count(scope => string.Equals(scope, decryptNewtonsoftScope, StringComparison.Ordinal)); + + return (streamEncrypt, streamDecrypt, newtonsoftEncrypt, newtonsoftDecrypt); + } + + [TestMethod] + public async Task ProvidedOutputDecrypt_StreamProcessor_SuccessAndRewinds() + { + TestItem testItem = new TestItem + { + Id = Guid.NewGuid().ToString(), + PK = Guid.NewGuid().ToString(), + NonSensitive = "po-plain", + Sensitive = "po-secret" + }; + + EncryptionItemRequestOptions options = CreateEncryptionItemRequestOptions( + dekProperties.Id, + CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + JsonProcessor.Stream); + + _ = await encryptionContainer.CreateItemAsync(testItem, new PartitionKey(testItem.PK), options); + + // IMPORTANT: use the base container to get the raw encrypted payload (_ei present). The encryption container would auto-decrypt. + using ResponseMessage readStream = await itemContainer.ReadItemStreamAsync(testItem.Id, new PartitionKey(testItem.PK)); + Assert.IsTrue(readStream.IsSuccessStatusCode); + + readStream.Content.Position = 0; + MemoryStream output = new(); + DecryptionContext ctx = await EncryptionProcessor.DecryptAsync( + readStream.Content, + output, + encryptor, + new CosmosDiagnosticsContext(), + new ItemRequestOptions { Properties = new Dictionary { { "encryption-json-processor", "Stream" } } }, + CancellationToken.None); + Assert.IsNotNull(ctx); + Assert.AreEqual(0, output.Position); // rewound for caller consumption + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public async Task ProvidedOutputDecrypt_StreamOverrideWithLegacyAlgorithm_Throws() + { + TestItem testItem = new TestItem + { + Id = Guid.NewGuid().ToString(), + PK = Guid.NewGuid().ToString(), + NonSensitive = "legacy-plain", + Sensitive = "legacy-secret" + }; + + EncryptionItemRequestOptions options = CreateEncryptionItemRequestOptions( + dekProperties.Id, +#pragma warning disable CS0618 + CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized, +#pragma warning restore CS0618 + JsonProcessor.Newtonsoft); + + _ = await encryptionContainer.CreateItemAsync(testItem, new PartitionKey(testItem.PK), options); + + // Get raw encrypted (legacy algorithm) payload without automatic decrypt. + using ResponseMessage readStream = await itemContainer.ReadItemStreamAsync(testItem.Id, new PartitionKey(testItem.PK)); + readStream.Content.Position = 0; + MemoryStream output = new(); + _ = await EncryptionProcessor.DecryptAsync( + readStream.Content, + output, + encryptor, + new CosmosDiagnosticsContext(), + new ItemRequestOptions { Properties = new Dictionary { { "encryption-json-processor", "Stream" } } }, + CancellationToken.None); + } + + private class TestItem + { + [JsonProperty("id")] public string Id { get; set; } + public string PK { get; set; } + public string NonSensitive { get; set; } + public string Sensitive { get; set; } + } +#endif + + private static EncryptionItemRequestOptions CreateEncryptionItemRequestOptions(string dekId, string encryptionAlgorithm, JsonProcessor jsonProcessor) + { + EncryptionItemRequestOptions options = new () + { + EncryptionOptions = new EncryptionOptions + { + DataEncryptionKeyId = dekId, + EncryptionAlgorithm = encryptionAlgorithm, + PathsToEncrypt = new List { "/Sensitive" }, + }, + Properties = new Dictionary + { + { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, jsonProcessor.ToString() } + } + }; + + return options; + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public async Task UnsupportedJsonProcessor_ThrowsNotSupportedException() + { + // This test validates that unsupported JsonProcessor values (e.g., Stream on net6.0/netstandard2.0) + // are properly rejected with a clear error message. + // We cast an int to JsonProcessor to simulate a value that doesn't exist on this platform. + TestDoc testDoc = TestDoc.Create(); + + // Cast 99 to JsonProcessor - this simulates an unsupported/future processor value + JsonProcessor unsupportedProcessor = (JsonProcessor)99; + + EncryptionItemRequestOptions options = new() + { + EncryptionOptions = new EncryptionOptions + { + DataEncryptionKeyId = dekId, + EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + PathsToEncrypt = TestDoc.PathsToEncrypt, + }, + Properties = new Dictionary + { + { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, unsupportedProcessor } + } + }; + + // This should throw NotSupportedException on all platforms for an unsupported processor value + await encryptionContainer.CreateItemAsync(testDoc, new PartitionKey(testDoc.PK), options); + } + [TestMethod] public async Task EncryptionCreateItemWithoutEncryptionOptions() { @@ -1560,6 +1984,15 @@ private static async Task ValidateChangeFeedProcessorWithFeedStreamHandlerRespon bool isStartOk = allDocsProcessed.WaitOne(60000); await cfp.StopAsync(); + Assert.IsTrue( + isStartOk, + "Change feed processor with stream handler did not observe the expected items before timing out."); + + Assert.AreEqual( + 2, + processedDocCount, + $"Expected stream handler to process both documents, but processed count was {processedDocCount}."); + if (leaseDatabase != null) { using (await leaseDatabase.DeleteStreamAsync()) { } @@ -1617,6 +2050,15 @@ private static async Task ValidateChangeFeedProcessorStreamWithManualCheckpointR bool isStartOk = allDocsProcessed.WaitOne(60000); await cfp.StopAsync(); + Assert.IsTrue( + isStartOk, + "Change feed processor stream handler with manual checkpoint did not observe the expected items before timing out."); + + Assert.AreEqual( + 2, + processedDocCount, + $"Expected stream handler with manual checkpoint to process both documents, but processed count was {processedDocCount}."); + if (leaseDatabase != null) { using (await leaseDatabase.DeleteStreamAsync()) { } @@ -2090,7 +2532,7 @@ public override bool Equals(object obj) && this.Sensitive_ArrayFormat == doc.Sensitive_ArrayFormat && this.Sensitive_BoolFormat == doc.Sensitive_BoolFormat && this.Sensitive_FloatFormat == doc.Sensitive_FloatFormat - && this.Sensitive_NestedObjectFormatL1 != doc.Sensitive_NestedObjectFormatL1; + && this.Sensitive_NestedObjectFormatL1 == doc.Sensitive_NestedObjectFormatL1; } public bool EqualsExceptEncryptedProperty(object obj) @@ -2667,3 +3109,4 @@ public override Task WrapKeyAsync(byte[] key, Encryptio #endregion } } + diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/EmulatorTests/Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests.csproj b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/EmulatorTests/Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests.csproj index bab1697c57..d45b8d91f2 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/EmulatorTests/Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests.csproj +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/EmulatorTests/Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests.csproj @@ -27,7 +27,7 @@ - + diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/EncryptionBenchmark.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/EncryptionBenchmark.cs index b436edd547..dbae723beb 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/EncryptionBenchmark.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/EncryptionBenchmark.cs @@ -3,7 +3,7 @@ using System.IO; using BenchmarkDotNet.Attributes; using Microsoft.Data.Encryption.Cryptography; -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +#if NET8_0_OR_GREATER using Microsoft.IO; #endif using Moq; @@ -11,7 +11,28 @@ [RPlotExporter] public partial class EncryptionBenchmark { - private static readonly byte[] DekData = Enumerable.Repeat((byte)0, 32).ToArray(); + private static class RequestOptionsOverrideHelper + { + public static RequestOptions? Create(JsonProcessor processor) + { +#if NET8_0_OR_GREATER + if (processor == JsonProcessor.Newtonsoft) + { + return null; + } + return new ItemRequestOptions + { + Properties = new System.Collections.Generic.Dictionary + { + { "encryption-json-processor", processor.ToString() } + } + }; +#else + return null; +#endif + } + } + private static readonly byte[] DekData = Enumerable.Repeat((byte)0, 32).ToArray(); private static readonly DataEncryptionKeyProperties DekProperties = new( "id", CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, @@ -19,7 +40,7 @@ public partial class EncryptionBenchmark new EncryptionKeyWrapMetadata("name", "value"), DateTime.UtcNow); private static readonly Mock StoreProvider = new(); -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +#if NET8_0_OR_GREATER private readonly RecyclableMemoryStreamManager recyclableMemoryStreamManager = new (); #endif @@ -32,15 +53,12 @@ public partial class EncryptionBenchmark [Params(1, 10, 100)] public int DocumentSizeInKb { get; set; } - [Params(CompressionOptions.CompressionAlgorithm.None, CompressionOptions.CompressionAlgorithm.Brotli)] - public CompressionOptions.CompressionAlgorithm CompressionAlgorithm { get; set; } - #if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER [Params(JsonProcessor.Newtonsoft, JsonProcessor.Stream)] #else [Params(JsonProcessor.Newtonsoft)] #endif - public JsonProcessor JsonProcessor { get; set; } + internal JsonProcessor JsonProcessor { get; set; } [GlobalSetup] public async Task Setup() @@ -52,7 +70,7 @@ public async Task Setup() Mock keyProvider = new(); keyProvider .Setup(x => x.FetchDataEncryptionKeyWithoutRawKeyAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(() => new MdeEncryptionAlgorithm(DekProperties, EncryptionType.Randomized, StoreProvider.Object, cacheTimeToLive: TimeSpan.MaxValue)); + .Returns(async () => await MdeEncryptionAlgorithm.CreateAsync(DekProperties, EncryptionType.Randomized, StoreProvider.Object, cacheTimeToLive: TimeSpan.MaxValue, false, default)); this.encryptor = new(keyProvider.Object); this.encryptionOptions = this.CreateEncryptionOptions(); @@ -62,6 +80,7 @@ public async Task Setup() new MemoryStream(this.plaintext), this.encryptor, this.encryptionOptions, + this.JsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); @@ -78,11 +97,12 @@ await EncryptionProcessor.EncryptAsync( new MemoryStream(this.plaintext!), this.encryptor, this.encryptionOptions, + this.JsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); } -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +#if NET8_0_OR_GREATER [Benchmark] public async Task EncryptToProvidedStream() { @@ -92,6 +112,7 @@ await EncryptionProcessor.EncryptAsync( rms, this.encryptor, this.encryptionOptions, + this.JsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); } @@ -104,11 +125,11 @@ await EncryptionProcessor.DecryptAsync( new MemoryStream(this.encryptedData!), this.encryptor, new CosmosDiagnosticsContext(), - this.JsonProcessor, + RequestOptionsOverrideHelper.Create(this.JsonProcessor), CancellationToken.None); } -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +#if NET8_0_OR_GREATER [Benchmark] public async Task DecryptToProvidedStream() { @@ -118,7 +139,7 @@ await EncryptionProcessor.DecryptAsync( rms, this.encryptor, new CosmosDiagnosticsContext(), - this.JsonProcessor, + RequestOptionsOverrideHelper.Create(this.JsonProcessor), CancellationToken.None); } #endif @@ -130,11 +151,6 @@ private EncryptionOptions CreateEncryptionOptions() DataEncryptionKeyId = "dekId", EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, PathsToEncrypt = TestDoc.PathsToEncrypt, - CompressionOptions = new() - { - Algorithm = this.CompressionAlgorithm - }, - JsonProcessor = this.JsonProcessor, }; return options; diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests.csproj b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests.csproj index 23f9b23530..4e3573f46d 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests.csproj +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests.csproj @@ -7,7 +7,6 @@ enable enable true - $(DefineConstants);ENCRYPTION_CUSTOM_PREVIEW @@ -18,14 +17,11 @@ + - - - - diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/ContractEnforcementTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/ContractEnforcementTests.cs index 27654c8400..52238d5223 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/ContractEnforcementTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/ContractEnforcementTests.cs @@ -7,13 +7,29 @@ [TestClass] public class ContractEnforcementTests { + /// + /// This test validates the public API surface against a baseline contract. + /// + /// IMPORTANT: Because the library multi-targets (netstandard2.0 + net8.0) and uses + /// conditional compilation (#if NET8_0_OR_GREATER), the API surface differs between + /// .NET versions. Therefore: + /// + /// - When running on net6.0: validates against DotNetSDKEncryptionCustomAPI.net6.json + /// - When running on net8.0: validates against DotNetSDKEncryptionCustomAPI.net8.json + /// + /// There is NO generic fallback - each supported framework MUST have its own baseline. + /// + /// To update baselines, run: UpdateContracts.ps1 from the repository root. + /// This script runs tests on BOTH net6.0 and net8.0 to generate both baselines. + /// [TestMethod] public void ContractChanges() { - Cosmos.Tests.Contracts.ContractEnforcement.ValidateContractContainBreakingChanges( + Cosmos.Tests.Contracts.ContractEnforcement.ValidateContract( dllName: "Microsoft.Azure.Cosmos.Encryption.Custom", - baselinePath: "DotNetSDKEncryptionCustomAPI.json", - breakingChangesPath: "DotNetSDKEncryptionCustomAPIChanges.json"); + contractType: Cosmos.Tests.Contracts.ContractType.Standard, + baselinePattern: "DotNetSDKEncryptionCustomAPI", + breakingChangesPattern: "DotNetSDKEncryptionCustomAPIChanges"); } } } diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.json b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.net6.json similarity index 72% rename from Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.json rename to Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.net6.json index 280be12c65..2c4f18f5ae 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.json +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.net6.json @@ -1,111 +1,5 @@ { "Subclasses": { - "Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { - "Subclasses": {}, - "Members": { - "CompressionAlgorithm Algorithm": { - "Type": "Property", - "Attributes": [], - "MethodInfo": "CompressionAlgorithm Algorithm;CanRead:True;CanWrite:True;CompressionAlgorithm get_Algorithm();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Algorithm(CompressionAlgorithm);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "CompressionAlgorithm get_Algorithm()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { - "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], - "MethodInfo": "CompressionAlgorithm get_Algorithm();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Int32 get_MinimalCompressedLength()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { - "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], - "MethodInfo": "Int32 get_MinimalCompressedLength();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Int32 MinimalCompressedLength": { - "Type": "Property", - "Attributes": [], - "MethodInfo": "Int32 MinimalCompressedLength;CanRead:True;CanWrite:True;Int32 get_MinimalCompressedLength();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_MinimalCompressedLength(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions+CompressionAlgorithm": { - "Type": "NestedType", - "Attributes": [], - "MethodInfo": null - }, - "System.IO.Compression.CompressionLevel CompressionLevel": { - "Type": "Property", - "Attributes": [], - "MethodInfo": "System.IO.Compression.CompressionLevel CompressionLevel;CanRead:True;CanWrite:True;System.IO.Compression.CompressionLevel get_CompressionLevel();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_CompressionLevel(System.IO.Compression.CompressionLevel);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.IO.Compression.CompressionLevel get_CompressionLevel()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { - "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], - "MethodInfo": "System.IO.Compression.CompressionLevel get_CompressionLevel();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Void .ctor()": { - "Type": "Constructor", - "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" - }, - "Void set_Algorithm(CompressionAlgorithm)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { - "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], - "MethodInfo": "Void set_Algorithm(CompressionAlgorithm);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Void set_CompressionLevel(System.IO.Compression.CompressionLevel)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { - "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], - "MethodInfo": "Void set_CompressionLevel(System.IO.Compression.CompressionLevel);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Void set_MinimalCompressedLength(Int32)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { - "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], - "MethodInfo": "Void set_MinimalCompressedLength(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - } - }, - "NestedTypes": { - "Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions+CompressionAlgorithm;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:True;IsGenericType:False;IsSerializable:True": { - "Subclasses": {}, - "Members": { - "CompressionAlgorithm None": { - "Type": "Field", - "Attributes": [], - "MethodInfo": "CompressionAlgorithm None;IsInitOnly:False;IsStatic:True;" - }, - "Int32 value__": { - "Type": "Field", - "Attributes": [], - "MethodInfo": "Int32 value__;IsInitOnly:False;IsStatic:False;" - } - }, - "NestedTypes": {} - } - } - }, - "Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions+CompressionAlgorithm;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:True;IsGenericType:False;IsSerializable:True": { - "Subclasses": {}, - "Members": { - "CompressionAlgorithm None": { - "Type": "Field", - "Attributes": [], - "MethodInfo": "CompressionAlgorithm None;IsInitOnly:False;IsStatic:True;" - }, - "Int32 value__": { - "Type": "Field", - "Attributes": [], - "MethodInfo": "Int32 value__;IsInitOnly:False;IsStatic:False;" - } - }, - "NestedTypes": {} - }, "Microsoft.Azure.Cosmos.Encryption.Custom.CosmosDataEncryptionKeyProvider;Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { @@ -169,19 +63,19 @@ "Attributes": [ "ObsoleteAttribute" ], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])[System.ObsoleteAttribute(\"Please use the constructor with EncryptionKeyStoreProvider only.\")], Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])" }, "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])[System.ObsoleteAttribute(\"Please use the constructor with EncryptionKeyStoreProvider only.\")]": { "Type": "Constructor", "Attributes": [ "ObsoleteAttribute" ], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])[System.ObsoleteAttribute(\"Please use the constructor with EncryptionKeyStoreProvider only.\")], Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])" }, "Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan]), Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])]" + "MethodInfo": "Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])" } }, "NestedTypes": {} @@ -231,60 +125,19 @@ "Attributes": [ "AsyncStateMachineAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] GetEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Byte[]] DecryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.DecryptData to reduce overhead.\")]": { - "Type": "Method", - "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" - ], "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] DecryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.EncryptData to reduce overhead.\")]": { + "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]": { "Type": "Method", "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" + "AsyncStateMachineAttribute" ], "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[System.Int32] DecryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.DecryptData to reduce overhead.\")]": { - "Type": "Method", - "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" - ], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] DecryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Int32] EncryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.EncryptData to reduce overhead.\")]": { - "Type": "Method", - "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" - ], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] EncryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Int32] GetDecryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.GetDecryptByteCount to reduce overhead.\")]": { - "Type": "Method", - "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" - ], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] GetDecryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Int32] GetEncryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.GetEncryptByteCount to reduce overhead.\")]": { - "Type": "Method", - "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" - ], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] GetEncryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider), Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider)" } }, "NestedTypes": {} @@ -302,11 +155,6 @@ "Attributes": [], "MethodInfo": "Byte[] EncryptData(Byte[]);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Byte[] Generate(System.String)": { - "Type": "Method", - "Attributes": [], - "MethodInfo": "Byte[] Generate(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, "Byte[] get_RawKey()": { "Type": "Method", "Attributes": [], @@ -337,10 +185,15 @@ "Attributes": [], "MethodInfo": "Int32 GetEncryptByteCount(Int32);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey Create(Byte[], System.String)": { + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey.Create(System.Byte[], System.String)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey Create(Byte[], System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey.Create(System.Byte[], System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Byte[] Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey.Generate(System.String)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Byte[] Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey.Generate(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "System.String EncryptionAlgorithm": { "Type": "Property", @@ -406,7 +259,7 @@ ], "MethodInfo": "Byte[] get_WrappedDataEncryptionKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Byte[] WrappedDataEncryptionKey[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"wrappedDataEncryptionKey\")]": { + "Byte[] WrappedDataEncryptionKey[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"wrappedDataEncryptionKey\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -418,7 +271,7 @@ "Attributes": [], "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"keyWrapMetadata\")]": { + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"keyWrapMetadata\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -432,7 +285,7 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata get_EncryptionKeyWrapMetadata();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.DateTime] CreatedTime[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"createTime\")]": { + "System.Nullable`1[System.DateTime] CreatedTime[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"createTime\")]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -454,7 +307,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_ts\")]": { + "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_ts\")]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -462,14 +315,14 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] LastModified;CanRead:True;CanWrite:True;System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String EncryptionAlgorithm[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"encryptionAlgorithm\")]": { + "System.String EncryptionAlgorithm[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"encryptionAlgorithm\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" ], "MethodInfo": "System.String EncryptionAlgorithm;CanRead:True;CanWrite:True;System.String get_EncryptionAlgorithm();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -511,7 +364,7 @@ ], "MethodInfo": "System.String Id;CanRead:True;CanWrite:True;System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -521,7 +374,7 @@ "Void .ctor(System.String, System.String, Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.DateTime)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.String, Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.DateTime), Void .ctor(System.String, System.String, Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.DateTime)]" + "MethodInfo": "Void .ctor(System.String, System.String, Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.DateTime)" } }, "NestedTypes": {} @@ -591,19 +444,19 @@ "Attributes": [ "ObsoleteAttribute" ], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])[System.ObsoleteAttribute(\"Please use the constructor with EncryptionKeyStoreProvider only.\")], Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])" }, "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])[System.ObsoleteAttribute(\"Please use the constructor with EncryptionKeyStoreProvider only.\")]": { "Type": "Constructor", "Attributes": [ "ObsoleteAttribute" ], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])[System.ObsoleteAttribute(\"Please use the constructor with EncryptionKeyStoreProvider only.\")], Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])" }, "Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan]), Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])]" + "MethodInfo": "Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])" } }, "NestedTypes": {} @@ -652,7 +505,7 @@ "Void .ctor(System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo]), Void .ctor(System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo])" } }, "NestedTypes": {} @@ -687,7 +540,7 @@ "Void .ctor(System.Collections.Generic.IReadOnlyList`1[System.String], System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IReadOnlyList`1[System.String], System.String), Void .ctor(System.Collections.Generic.IReadOnlyList`1[System.String], System.String)]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IReadOnlyList`1[System.String], System.String)" } }, "NestedTypes": {} @@ -752,7 +605,7 @@ "Void .ctor(T)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(T), Void .ctor(T)]" + "MethodInfo": "Void .ctor(T)" } }, "NestedTypes": {} @@ -785,7 +638,7 @@ "Void .ctor(System.IO.Stream)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.IO.Stream), Void .ctor(System.IO.Stream)]" + "MethodInfo": "Void .ctor(System.IO.Stream)" }, "Void Dispose()": { "Type": "Method", @@ -838,7 +691,7 @@ "Void .ctor(T)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(T), Void .ctor(T)]" + "MethodInfo": "Void .ctor(T)" } }, "NestedTypes": {} @@ -871,7 +724,7 @@ "Void .ctor(System.IO.Stream)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.IO.Stream), Void .ctor(System.IO.Stream)]" + "MethodInfo": "Void .ctor(System.IO.Stream)" }, "Void Dispose()": { "Type": "Method", @@ -884,26 +737,26 @@ "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "Microsoft.Azure.Cosmos.Container WithEncryptor(Microsoft.Azure.Cosmos.Container, Microsoft.Azure.Cosmos.Encryption.Custom.Encryptor)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Microsoft.Azure.Cosmos.Container Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.WithEncryptor(Microsoft.Azure.Cosmos.Container, Microsoft.Azure.Cosmos.Encryption.Custom.Encryptor)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Microsoft.Azure.Cosmos.Container WithEncryptor(Microsoft.Azure.Cosmos.Container, Microsoft.Azure.Cosmos.Encryption.Custom.Encryptor);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Container Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.WithEncryptor(Microsoft.Azure.Cosmos.Container, Microsoft.Azure.Cosmos.Encryption.Custom.Encryptor);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.FeedIterator ToEncryptionStreamIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Microsoft.Azure.Cosmos.FeedIterator Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.ToEncryptionStreamIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator ToEncryptionStreamIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.ToEncryptionStreamIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.FeedIterator`1[T] ToEncryptionFeedIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Microsoft.Azure.Cosmos.FeedIterator`1[T] Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.ToEncryptionFeedIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator`1[T] ToEncryptionFeedIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator`1[T] Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.ToEncryptionFeedIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -938,7 +791,7 @@ "Void .ctor(System.String, System.String, System.Exception)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.String, System.Exception), Void .ctor(System.String, System.String, System.Exception)]" + "MethodInfo": "Void .ctor(System.String, System.String, System.Exception)" } }, "NestedTypes": {} @@ -961,7 +814,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_EncryptionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -1003,7 +856,7 @@ "Void .ctor(Byte[], System.TimeSpan)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Byte[], System.TimeSpan), Void .ctor(Byte[], System.TimeSpan)]" + "MethodInfo": "Void .ctor(Byte[], System.TimeSpan)" } }, "NestedTypes": {} @@ -1040,14 +893,14 @@ ], "MethodInfo": "System.String get_Value();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String Name[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"name\")]": { + "System.String Name[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"name\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" ], "MethodInfo": "System.String Name;CanRead:True;CanWrite:True;System.String get_Name();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String Value[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"value\")]": { + "System.String Value[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"value\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -1057,17 +910,17 @@ "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata), Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)" }, "Void .ctor(System.String, System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.String), Void .ctor(System.String, System.String)]" + "MethodInfo": "Void .ctor(System.String, System.String)" }, "Void .ctor(System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String), Void .ctor(System.String)]" + "MethodInfo": "Void .ctor(System.String)" } }, "NestedTypes": {} @@ -1118,7 +971,7 @@ "Void .ctor(Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata), Void .ctor(Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)]" + "MethodInfo": "Void .ctor(Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)" } }, "NestedTypes": {} @@ -1126,30 +979,6 @@ "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions CompressionOptions": { - "Type": "Property", - "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions CompressionOptions;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions get_CompressionOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_CompressionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions get_CompressionOptions()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { - "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], - "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions get_CompressionOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor get_JsonProcessor()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { - "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], - "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor get_JsonProcessor();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor JsonProcessor": { - "Type": "Property", - "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor JsonProcessor;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor get_JsonProcessor();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_JsonProcessor(Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, "System.Collections.Generic.IEnumerable`1[System.String] get_PathsToEncrypt()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -1189,14 +1018,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" - }, - "Void set_CompressionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { - "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], - "MethodInfo": "Void set_CompressionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.CompressionOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Void .ctor()" }, "Void set_DataEncryptionKeyId(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -1212,13 +1034,6 @@ ], "MethodInfo": "Void set_EncryptionAlgorithm(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Void set_JsonProcessor(Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { - "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], - "MethodInfo": "Void set_JsonProcessor(Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, "Void set_PathsToEncrypt(System.Collections.Generic.IEnumerable`1[System.String])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -1247,7 +1062,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_EncryptionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -1288,60 +1103,19 @@ "Attributes": [ "AsyncStateMachineAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] GetEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Byte[]] DecryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.DecryptData to reduce overhead.\")]": { - "Type": "Method", - "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" - ], "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] DecryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.EncryptData to reduce overhead.\")]": { + "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]": { "Type": "Method", "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" + "AsyncStateMachineAttribute" ], "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[System.Int32] DecryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.DecryptData to reduce overhead.\")]": { - "Type": "Method", - "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" - ], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] DecryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Int32] EncryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.EncryptData to reduce overhead.\")]": { - "Type": "Method", - "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" - ], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] EncryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Int32] GetDecryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.GetDecryptByteCount to reduce overhead.\")]": { - "Type": "Method", - "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" - ], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] GetDecryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Int32] GetEncryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]-[System.ObsoleteAttribute(\"It is suggested to use GetEncryptionKeyAsync + key.GetEncryptByteCount to reduce overhead.\")]": { - "Type": "Method", - "Attributes": [ - "AsyncStateMachineAttribute", - "ObsoleteAttribute" - ], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] GetEncryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider), Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider)" } }, "NestedTypes": {} @@ -1362,83 +1136,31 @@ "Type": "Method", "Attributes": [], "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Int32] DecryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken)": { - "Type": "Method", - "Attributes": [], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] DecryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Int32] EncryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken)": { - "Type": "Method", - "Attributes": [], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] EncryptAsync(Byte[], Int32, Int32, Byte[], Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Int32] GetDecryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken)": { - "Type": "Method", - "Attributes": [], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] GetDecryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "System.Threading.Tasks.Task`1[System.Int32] GetEncryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken)": { - "Type": "Method", - "Attributes": [], - "MethodInfo": "System.Threading.Tasks.Task`1[System.Int32] GetEncryptBytesCountAsync(Int32, System.String, System.String, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - } - }, - "NestedTypes": {} - }, - "Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:False;IsGenericType:False;IsSerializable:True": { - "Subclasses": {}, - "Members": { - "Int32 value__": { - "Type": "Field", - "Attributes": [], - "MethodInfo": "Int32 value__;IsInitOnly:False;IsStatic:False;" - }, - "Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor Newtonsoft": { - "Type": "Field", - "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor Newtonsoft;IsInitOnly:False;IsStatic:True;" - } - }, - "NestedTypes": {} - }, - "Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:False;IsGenericType:False;IsSerializable:True": { - "Subclasses": {}, - "Members": { - "Int32 value__": { - "Type": "Field", - "Attributes": [], - "MethodInfo": "Int32 value__;IsInitOnly:False;IsStatic:False;" - }, - "Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor Newtonsoft": { - "Type": "Field", - "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.JsonProcessor Newtonsoft;IsInitOnly:False;IsStatic:True;" } }, "NestedTypes": {} } }, "Members": { - "Boolean Equals(System.Object, System.Object)": { + "Boolean Equals(System.Object)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Boolean Equals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean Equals(System.Object)": { + "Int32 GetHashCode()": { "Type": "Method", "Attributes": [], - "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean ReferenceEquals(System.Object, System.Object)": { + "System.Boolean System.Object.Equals(System.Object, System.Object)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Boolean ReferenceEquals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean System.Object.Equals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Int32 GetHashCode()": { + "System.Boolean System.Object.ReferenceEquals(System.Object, System.Object)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean System.Object.ReferenceEquals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "System.String ToString()": { "Type": "Method", @@ -1456,7 +1178,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.net8.json b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.net8.json new file mode 100644 index 0000000000..2c4f18f5ae --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Contracts/DotNetSDKEncryptionCustomAPI.net8.json @@ -0,0 +1,1185 @@ +{ + "Subclasses": { + "Microsoft.Azure.Cosmos.Encryption.Custom.CosmosDataEncryptionKeyProvider;Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer DataEncryptionKeyContainer": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer DataEncryptionKeyContainer;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer get_DataEncryptionKeyContainer();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer get_DataEncryptionKeyContainer()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer get_DataEncryptionKeyContainer();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider EncryptionKeyWrapProvider": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider EncryptionKeyWrapProvider;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider get_EncryptionKeyWrapProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider get_EncryptionKeyWrapProvider()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider get_EncryptionKeyWrapProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider EncryptionKeyStoreProvider": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider EncryptionKeyStoreProvider;CanRead:True;CanWrite:False;Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider get_EncryptionKeyStoreProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider get_EncryptionKeyStoreProvider()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider get_EncryptionKeyStoreProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task InitializeAsync(Microsoft.Azure.Cosmos.Database, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosDataEncryptionKeyProvider+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task InitializeAsync(Microsoft.Azure.Cosmos.Database, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosDataEncryptionKeyProvider+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyWithoutRawKeyAsync(System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosDataEncryptionKeyProvider+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyWithoutRawKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])[System.ObsoleteAttribute(\"Please use the constructor with EncryptionKeyStoreProvider only.\")]": { + "Type": "Constructor", + "Attributes": [ + "ObsoleteAttribute" + ], + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])" + }, + "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])[System.ObsoleteAttribute(\"Please use the constructor with EncryptionKeyStoreProvider only.\")]": { + "Type": "Constructor", + "Attributes": [ + "ObsoleteAttribute" + ], + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])" + }, + "Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptionAlgorithm;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String AEAes256CbcHmacSha256Randomized[System.ObsoleteAttribute(\"Please use MdeAeadAes256CbcHmac256Randomized.\")]": { + "Type": "Field", + "Attributes": [ + "ObsoleteAttribute" + ], + "MethodInfo": "System.String AEAes256CbcHmacSha256Randomized;IsInitOnly:False;IsStatic:True;" + }, + "System.String MdeAeadAes256CbcHmac256Randomized": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String MdeAeadAes256CbcHmac256Randomized;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor;Microsoft.Azure.Cosmos.Encryption.Custom.Encryptor;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider DataEncryptionKeyProvider": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider DataEncryptionKeyProvider;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider get_DataEncryptionKeyProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider get_DataEncryptionKeyProvider()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider get_DataEncryptionKeyProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] GetEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] GetEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] DecryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] DecryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Byte[] DecryptData(Byte[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Byte[] DecryptData(Byte[]);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Byte[] EncryptData(Byte[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Byte[] EncryptData(Byte[]);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Byte[] get_RawKey()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Byte[] get_RawKey();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Byte[] RawKey": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Byte[] RawKey;CanRead:True;CanWrite:False;Byte[] get_RawKey();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 DecryptData(Byte[], Int32, Int32, Byte[], Int32)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 DecryptData(Byte[], Int32, Int32, Byte[], Int32);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 EncryptData(Byte[], Int32, Int32, Byte[], Int32)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 EncryptData(Byte[], Int32, Int32, Byte[], Int32);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetDecryptByteCount(Int32)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetDecryptByteCount(Int32);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetEncryptByteCount(Int32)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetEncryptByteCount(Int32);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey.Create(System.Byte[], System.String)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey.Create(System.Byte[], System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Byte[] Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey.Generate(System.String)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Byte[] Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey.Generate(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String EncryptionAlgorithm": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String EncryptionAlgorithm;CanRead:True;CanWrite:False;System.String get_EncryptionAlgorithm();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_EncryptionAlgorithm()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.String get_EncryptionAlgorithm();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.FeedIterator`1[T] GetDataEncryptionKeyQueryIterator[T](Microsoft.Azure.Cosmos.QueryDefinition, System.String, Microsoft.Azure.Cosmos.QueryRequestOptions)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator`1[T] GetDataEncryptionKeyQueryIterator[T](Microsoft.Azure.Cosmos.QueryDefinition, System.String, Microsoft.Azure.Cosmos.QueryRequestOptions);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.FeedIterator`1[T] GetDataEncryptionKeyQueryIterator[T](System.String, System.String, Microsoft.Azure.Cosmos.QueryRequestOptions)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator`1[T] GetDataEncryptionKeyQueryIterator[T](System.String, System.String, Microsoft.Azure.Cosmos.QueryRequestOptions);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ItemResponse`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProperties]] CreateDataEncryptionKeyAsync(System.String, System.String, Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, Microsoft.Azure.Cosmos.ItemRequestOptions, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ItemResponse`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProperties]] CreateDataEncryptionKeyAsync(System.String, System.String, Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, Microsoft.Azure.Cosmos.ItemRequestOptions, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ItemResponse`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProperties]] ReadDataEncryptionKeyAsync(System.String, Microsoft.Azure.Cosmos.ItemRequestOptions, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ItemResponse`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProperties]] ReadDataEncryptionKeyAsync(System.String, Microsoft.Azure.Cosmos.ItemRequestOptions, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ItemResponse`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProperties]] RewrapDataEncryptionKeyAsync(System.String, Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.String, Microsoft.Azure.Cosmos.ItemRequestOptions, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ItemResponse`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProperties]] RewrapDataEncryptionKeyAsync(System.String, Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.String, Microsoft.Azure.Cosmos.ItemRequestOptions, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProperties;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Boolean Equals(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProperties)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean Equals(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProperties);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:True;" + }, + "Boolean Equals(System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Byte[] get_WrappedDataEncryptionKey()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Byte[] get_WrappedDataEncryptionKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Byte[] WrappedDataEncryptionKey[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"wrappedDataEncryptionKey\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "Byte[] WrappedDataEncryptionKey;CanRead:True;CanWrite:True;Byte[] get_WrappedDataEncryptionKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetHashCode()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"keyWrapMetadata\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata get_EncryptionKeyWrapMetadata();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata get_EncryptionKeyWrapMetadata()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata get_EncryptionKeyWrapMetadata();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.DateTime] CreatedTime[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"createTime\")]": { + "Type": "Property", + "Attributes": [ + "JsonConverterAttribute", + "JsonPropertyAttribute" + ], + "MethodInfo": "System.Nullable`1[System.DateTime] CreatedTime;CanRead:True;CanWrite:True;System.Nullable`1[System.DateTime] get_CreatedTime();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.DateTime] get_CreatedTime()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[System.DateTime] get_CreatedTime();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.DateTime] get_LastModified()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_ts\")]": { + "Type": "Property", + "Attributes": [ + "JsonConverterAttribute", + "JsonPropertyAttribute" + ], + "MethodInfo": "System.Nullable`1[System.DateTime] LastModified;CanRead:True;CanWrite:True;System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String EncryptionAlgorithm[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"encryptionAlgorithm\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String EncryptionAlgorithm;CanRead:True;CanWrite:True;System.String get_EncryptionAlgorithm();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String ETag;CanRead:True;CanWrite:True;System.String get_ETag();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_EncryptionAlgorithm()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_EncryptionAlgorithm();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_ETag()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_ETag();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_Id()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_SelfLink()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_SelfLink();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String Id[Newtonsoft.Json.JsonPropertyAttribute(PropertyName = \"id\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String Id;CanRead:True;CanWrite:True;System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String SelfLink;CanRead:True;CanWrite:True;System.String get_SelfLink();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(System.String, System.String, Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.DateTime)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.String, System.String, Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.DateTime)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": { + "Microsoft.Azure.Cosmos.Encryption.Custom.CosmosDataEncryptionKeyProvider;Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer DataEncryptionKeyContainer": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer DataEncryptionKeyContainer;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer get_DataEncryptionKeyContainer();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer get_DataEncryptionKeyContainer()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyContainer get_DataEncryptionKeyContainer();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider EncryptionKeyWrapProvider": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider EncryptionKeyWrapProvider;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider get_EncryptionKeyWrapProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider get_EncryptionKeyWrapProvider()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider get_EncryptionKeyWrapProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider EncryptionKeyStoreProvider": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider EncryptionKeyStoreProvider;CanRead:True;CanWrite:False;Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider get_EncryptionKeyStoreProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider get_EncryptionKeyStoreProvider()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider get_EncryptionKeyStoreProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task InitializeAsync(Microsoft.Azure.Cosmos.Database, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosDataEncryptionKeyProvider+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task InitializeAsync(Microsoft.Azure.Cosmos.Database, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosDataEncryptionKeyProvider+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyWithoutRawKeyAsync(System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosDataEncryptionKeyProvider+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyWithoutRawKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])[System.ObsoleteAttribute(\"Please use the constructor with EncryptionKeyStoreProvider only.\")]": { + "Type": "Constructor", + "Attributes": [ + "ObsoleteAttribute" + ], + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])" + }, + "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])[System.ObsoleteAttribute(\"Please use the constructor with EncryptionKeyStoreProvider only.\")]": { + "Type": "Constructor", + "Attributes": [ + "ObsoleteAttribute" + ], + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider, System.Nullable`1[System.TimeSpan])" + }, + "Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider, System.Nullable`1[System.TimeSpan])" + } + }, + "NestedTypes": {} + } + }, + "Members": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyWithoutRawKeyAsync(System.String, System.String, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] FetchDataEncryptionKeyWithoutRawKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Threading.Tasks.Task`1[System.ValueTuple`2[T,Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionContext]] GetItemAsync[T]()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.ValueTuple`2[T,Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionContext]] GetItemAsync[T]();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionContext;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo] DecryptionInfoList": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo] DecryptionInfoList;CanRead:True;CanWrite:False;System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo] get_DecryptionInfoList();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo] get_DecryptionInfoList()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo] get_DecryptionInfoList();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo])": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo])" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionInfo;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Collections.Generic.IReadOnlyList`1[System.String] get_PathsDecrypted()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Collections.Generic.IReadOnlyList`1[System.String] get_PathsDecrypted();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.IReadOnlyList`1[System.String] PathsDecrypted": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Collections.Generic.IReadOnlyList`1[System.String] PathsDecrypted;CanRead:True;CanWrite:False;System.Collections.Generic.IReadOnlyList`1[System.String] get_PathsDecrypted();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String DataEncryptionKeyId": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String DataEncryptionKeyId;CanRead:True;CanWrite:False;System.String get_DataEncryptionKeyId();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_DataEncryptionKeyId()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_DataEncryptionKeyId();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(System.Collections.Generic.IReadOnlyList`1[System.String], System.String)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.Collections.Generic.IReadOnlyList`1[System.String], System.String)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptionResult;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Exception Exception": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Exception Exception;CanRead:True;CanWrite:False;System.Exception get_Exception();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Exception get_Exception()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Exception get_Exception();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.ReadOnlyMemory`1[System.Byte] EncryptedStream": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.ReadOnlyMemory`1[System.Byte] EncryptedStream;CanRead:True;CanWrite:False;System.ReadOnlyMemory`1[System.Byte] get_EncryptedStream();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.ReadOnlyMemory`1[System.Byte] get_EncryptedStream()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.ReadOnlyMemory`1[System.Byte] get_EncryptedStream();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptableItem;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": { + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptableItem`1;Microsoft.Azure.Cosmos.Encryption.Custom.EncryptableItem;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:True;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem DecryptableItem": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem DecryptableItem;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "T get_Item()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "T get_Item();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "T Item": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "T Item;CanRead:True;CanWrite:False;T get_Item();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(T)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(T)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptableItemStream;Microsoft.Azure.Cosmos.Encryption.Custom.EncryptableItem;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem DecryptableItem": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem DecryptableItem;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.IO.Stream get_StreamPayload()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.IO.Stream get_StreamPayload();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.IO.Stream StreamPayload": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.IO.Stream StreamPayload;CanRead:True;CanWrite:False;System.IO.Stream get_StreamPayload();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(System.IO.Stream)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.IO.Stream)" + }, + "Void Dispose()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void Dispose();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:True;" + } + }, + "NestedTypes": {} + } + }, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem DecryptableItem": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem DecryptableItem;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptableItem`1;Microsoft.Azure.Cosmos.Encryption.Custom.EncryptableItem;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:True;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem DecryptableItem": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem DecryptableItem;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "T get_Item()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "T get_Item();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "T Item": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "T Item;CanRead:True;CanWrite:False;T get_Item();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(T)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(T)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptableItemStream;Microsoft.Azure.Cosmos.Encryption.Custom.EncryptableItem;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem DecryptableItem": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem DecryptableItem;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DecryptableItem get_DecryptableItem();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.IO.Stream get_StreamPayload()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.IO.Stream get_StreamPayload();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.IO.Stream StreamPayload": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.IO.Stream StreamPayload;CanRead:True;CanWrite:False;System.IO.Stream get_StreamPayload();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(System.IO.Stream)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.IO.Stream)" + }, + "Void Dispose()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void Dispose();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Container Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.WithEncryptor(Microsoft.Azure.Cosmos.Container, Microsoft.Azure.Cosmos.Encryption.Custom.Encryptor)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.Container Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.WithEncryptor(Microsoft.Azure.Cosmos.Container, Microsoft.Azure.Cosmos.Encryption.Custom.Encryptor);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.FeedIterator Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.ToEncryptionStreamIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.ToEncryptionStreamIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.FeedIterator`1[T] Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.ToEncryptionFeedIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator`1[T] Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionContainerExtensions.ToEncryptionFeedIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionException;System.Exception;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String DataEncryptionKeyId": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String DataEncryptionKeyId;CanRead:True;CanWrite:False;System.String get_DataEncryptionKeyId();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String EncryptedContent": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String EncryptedContent;CanRead:True;CanWrite:False;System.String get_EncryptedContent();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_DataEncryptionKeyId()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_DataEncryptionKeyId();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_EncryptedContent()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_EncryptedContent();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(System.String, System.String, System.Exception)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.String, System.String, System.Exception)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionItemRequestOptions;Microsoft.Azure.Cosmos.ItemRequestOptions;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions EncryptionOptions": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions EncryptionOptions;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions get_EncryptionOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_EncryptionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions get_EncryptionOptions()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions get_EncryptionOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + }, + "Void set_EncryptionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_EncryptionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyUnwrapResult;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Byte[] DataEncryptionKey": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Byte[] DataEncryptionKey;CanRead:True;CanWrite:False;Byte[] get_DataEncryptionKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Byte[] get_DataEncryptionKey()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Byte[] get_DataEncryptionKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.TimeSpan ClientCacheTimeToLive": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.TimeSpan ClientCacheTimeToLive;CanRead:True;CanWrite:False;System.TimeSpan get_ClientCacheTimeToLive();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.TimeSpan get_ClientCacheTimeToLive()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.TimeSpan get_ClientCacheTimeToLive();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(Byte[], System.TimeSpan)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(Byte[], System.TimeSpan)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Boolean Equals(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean Equals(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:True;" + }, + "Boolean Equals(System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetHashCode()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_Name()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_Name();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_Value()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_Value();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String Name[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"name\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String Name;CanRead:True;CanWrite:True;System.String get_Name();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String Value[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"value\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String Value;CanRead:True;CanWrite:True;System.String get_Value();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)" + }, + "Void .ctor(System.String, System.String)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.String, System.String)" + }, + "Void .ctor(System.String)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.String)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapProvider;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyUnwrapResult] UnwrapKeyAsync(Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyUnwrapResult] UnwrapKeyAsync(Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapResult] WrapKeyAsync(Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapResult] WrapKeyAsync(Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapResult;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Byte[] get_WrappedDataEncryptionKey()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Byte[] get_WrappedDataEncryptionKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Byte[] WrappedDataEncryptionKey": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Byte[] WrappedDataEncryptionKey;CanRead:True;CanWrite:False;Byte[] get_WrappedDataEncryptionKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata get_EncryptionKeyWrapMetadata();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata get_EncryptionKeyWrapMetadata()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata get_EncryptionKeyWrapMetadata();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(Byte[], Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionKeyWrapMetadata)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Collections.Generic.IEnumerable`1[System.String] get_PathsToEncrypt()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Collections.Generic.IEnumerable`1[System.String] get_PathsToEncrypt();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.IEnumerable`1[System.String] PathsToEncrypt": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Collections.Generic.IEnumerable`1[System.String] PathsToEncrypt;CanRead:True;CanWrite:True;System.Collections.Generic.IEnumerable`1[System.String] get_PathsToEncrypt();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_PathsToEncrypt(System.Collections.Generic.IEnumerable`1[System.String]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String DataEncryptionKeyId": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String DataEncryptionKeyId;CanRead:True;CanWrite:True;System.String get_DataEncryptionKeyId();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_DataEncryptionKeyId(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String EncryptionAlgorithm": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String EncryptionAlgorithm;CanRead:True;CanWrite:True;System.String get_EncryptionAlgorithm();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_EncryptionAlgorithm(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_DataEncryptionKeyId()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_DataEncryptionKeyId();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_EncryptionAlgorithm()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_EncryptionAlgorithm();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + }, + "Void set_DataEncryptionKeyId(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_DataEncryptionKeyId(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_EncryptionAlgorithm(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_EncryptionAlgorithm(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_PathsToEncrypt(System.Collections.Generic.IEnumerable`1[System.String])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_PathsToEncrypt(System.Collections.Generic.IEnumerable`1[System.String]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionTransactionalBatchItemRequestOptions;Microsoft.Azure.Cosmos.TransactionalBatchItemRequestOptions;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions EncryptionOptions": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions EncryptionOptions;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions get_EncryptionOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_EncryptionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions get_EncryptionOptions()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions get_EncryptionOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + }, + "Void set_EncryptionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_EncryptionOptions(Microsoft.Azure.Cosmos.Encryption.Custom.EncryptionOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.Encryptor;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": { + "Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor;Microsoft.Azure.Cosmos.Encryption.Custom.Encryptor;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider DataEncryptionKeyProvider": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider DataEncryptionKeyProvider;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider get_DataEncryptionKeyProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider get_DataEncryptionKeyProvider()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider get_DataEncryptionKeyProvider();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] GetEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] GetEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] DecryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] DecryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.Custom.CosmosEncryptor+))]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKeyProvider)" + } + }, + "NestedTypes": {} + } + }, + "Members": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] GetEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Encryption.Custom.DataEncryptionKey] GetEncryptionKeyAsync(System.String, System.String, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] DecryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] DecryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] EncryptAsync(Byte[], System.String, System.String, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + } + }, + "Members": { + "Boolean Equals(System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetHashCode()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Boolean System.Object.Equals(System.Object, System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Boolean System.Object.Equals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Boolean System.Object.ReferenceEquals(System.Object, System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Boolean System.Object.ReferenceEquals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String ToString()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.String ToString();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Type GetType()[System.Runtime.CompilerServices.NullableContextAttribute((Byte)1)]-[System.Runtime.CompilerServices.IntrinsicAttribute()]": { + "Type": "Method", + "Attributes": [ + "IntrinsicAttribute", + "NullableContextAttribute" + ], + "MethodInfo": "System.Type GetType();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + } + }, + "NestedTypes": {} +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/CosmosDiagnosticsContextProductionTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/CosmosDiagnosticsContextProductionTests.cs new file mode 100644 index 0000000000..be178dd0e9 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/CosmosDiagnosticsContextProductionTests.cs @@ -0,0 +1,333 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Tests +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + /// + /// Tests for the production CosmosDiagnosticsContext that verify Activity integration. + /// These tests directly test the production code's OpenTelemetry Activity creation and disposal. + /// + [TestClass] + public class CosmosDiagnosticsContextProductionTests + { + [TestMethod] + public void Create_WithNullOptions_ReturnsValidContext() + { + // Act + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + + // Assert + Assert.IsNotNull(context); + } + + [TestMethod] + public void CreateScope_WithActivityListener_CreatesActivity() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + List capturedActivities = new List(); + + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => { lock (capturedActivities) { capturedActivities.Add(activity); } } + }; + ActivitySource.AddActivityListener(listener); + + // Act + using (context.CreateScope("TestActivity")) + { + // Activity should be active + } + + // Assert + lock (capturedActivities) + { + Assert.AreEqual(1, capturedActivities.Count, "Expected one Activity to be created"); + Assert.AreEqual("TestActivity", capturedActivities[0].DisplayName); + Assert.AreEqual(ActivityKind.Internal, capturedActivities[0].Kind); + } + } + + [TestMethod] + public void CreateScope_WithoutActivityListener_DoesNotThrow() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + + // Act & Assert - Should not throw even without a listener + using (context.CreateScope("NoListenerScope")) + { + // No exception should be thrown + } + } + + [TestMethod] + public void Scope_Dispose_DisposesActivity() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + Activity stoppedActivity = null; + + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStopped = activity => stoppedActivity = activity + }; + ActivitySource.AddActivityListener(listener); + + // Act + using (CosmosDiagnosticsContext.Scope scope = context.CreateScope("DisposableActivity")) + { + // Activity created + } // Activity should be disposed here + + // Assert + Assert.IsNotNull(stoppedActivity, "Activity should have been stopped/disposed"); + Assert.AreEqual("DisposableActivity", stoppedActivity.DisplayName); + } + + [TestMethod] + public void Scope_DisposeIdempotent_OnlyDisposesActivityOnce() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + int stopCount = 0; + + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStopped = activity => Interlocked.Increment(ref stopCount) + }; + ActivitySource.AddActivityListener(listener); + + // Act + CosmosDiagnosticsContext.Scope scope = context.CreateScope("IdempotentActivity"); + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + + // Assert + Assert.AreEqual(1, stopCount, "Activity should only be stopped once despite multiple Dispose calls"); + } + + [TestMethod] + public void CreateScope_WithNullScope_ThrowsArgumentNullException() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsException(() => + { + context.CreateScope(null); + }); + + Assert.AreEqual("scope", ex.ParamName); + } + + [TestMethod] + public void CreateScope_WithEmptyScope_ThrowsArgumentException() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + + // Act & Assert + ArgumentException ex = Assert.ThrowsException(() => + { + context.CreateScope(string.Empty); + }); + + Assert.AreEqual("scope", ex.ParamName); + } + + [TestMethod] + public void CreateScope_WithWhitespaceScope_ThrowsArgumentException() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + + // Act & Assert + ArgumentException ex = Assert.ThrowsException(() => + { + context.CreateScope(" "); + }); + + Assert.AreEqual("scope", ex.ParamName); + } + + [TestMethod] + public void NestedScopes_CreatesNestedActivities() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + List activityNames = new List(); + + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => { lock (activityNames) { activityNames.Add(activity.DisplayName); } } + }; + ActivitySource.AddActivityListener(listener); + + // Act + using (context.CreateScope("Outer")) + { + using (context.CreateScope("Inner")) + { + // Nested scope + } + } + + // Assert + lock (activityNames) + { + Assert.AreEqual(2, activityNames.Count); + Assert.AreEqual("Outer", activityNames[0]); + Assert.AreEqual("Inner", activityNames[1]); + } + } + + [TestMethod] + public void ConcurrentScopes_CreateIndependentActivities() + { + // Arrange + CosmosDiagnosticsContext context1 = CosmosDiagnosticsContext.Create(null); + CosmosDiagnosticsContext context2 = CosmosDiagnosticsContext.Create(null); + List activityNames = new List(); + + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => { lock (activityNames) { activityNames.Add(activity.DisplayName); } } + }; + ActivitySource.AddActivityListener(listener); + + // Act + using (context1.CreateScope("Context1Scope")) + { + } + using (context2.CreateScope("Context2Scope")) + { + } + + // Assert + lock (activityNames) + { + Assert.AreEqual(2, activityNames.Count); + Assert.IsTrue(activityNames.Contains("Context1Scope")); + Assert.IsTrue(activityNames.Contains("Context2Scope")); + } + } + + [TestMethod] + public void ScopePrefixes_CreateCorrectActivityNames() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + List activityNames = new List(); + + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => { lock (activityNames) { activityNames.Add(activity.DisplayName); } } + }; + ActivitySource.AddActivityListener(listener); + + // Act + string encryptScope = CosmosDiagnosticsContext.ScopeEncryptModeSelectionPrefix + "Stream"; + string decryptScope = CosmosDiagnosticsContext.ScopeDecryptModeSelectionPrefix + "Newtonsoft"; + + using (context.CreateScope(encryptScope)) + { + } + using (context.CreateScope(decryptScope)) + { + } + + // Assert + lock (activityNames) + { + Assert.AreEqual(2, activityNames.Count); + Assert.AreEqual("EncryptionProcessor.Encrypt.Mde.Stream", activityNames[0]); + Assert.AreEqual("EncryptionProcessor.Decrypt.Mde.Newtonsoft", activityNames[1]); + } + } + + [TestMethod] + public void MultipleScopes_WithSameName_EachCreatesActivity() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + int activityCount = 0; + + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => Interlocked.Increment(ref activityCount) + }; + ActivitySource.AddActivityListener(listener); + + // Act + using (context.CreateScope("RepeatedScope")) + { + } + using (context.CreateScope("RepeatedScope")) + { + } + using (context.CreateScope("RepeatedScope")) + { + } + + // Assert + Assert.AreEqual(3, activityCount, "Each CreateScope call should create a new Activity"); + } + + [TestMethod] + public void Scope_WithException_StillDisposesActivity() + { + // Arrange + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + Activity stoppedActivity = null; + + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStopped = activity => stoppedActivity = activity + }; + ActivitySource.AddActivityListener(listener); + + // Act & Assert + try + { + using (context.CreateScope("ExceptionScope")) + { + throw new InvalidOperationException("Test exception"); + } + } + catch (InvalidOperationException) + { + // Expected + } + + // Assert - Activity should still be disposed even with exception + Assert.IsNotNull(stoppedActivity, "Activity should be disposed even when exception occurs"); + Assert.AreEqual("ExceptionScope", stoppedActivity.DisplayName); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/CosmosDiagnosticsContextTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/CosmosDiagnosticsContextTests.cs new file mode 100644 index 0000000000..b9a595f7e7 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/CosmosDiagnosticsContextTests.cs @@ -0,0 +1,826 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Tests +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class CosmosDiagnosticsContextTests + { + [TestMethod] + public void Create_WithNullOptions_ReturnsValidContext() + { + // Act + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + + // Assert + Assert.IsNotNull(context); + Assert.IsNotNull(context.Scopes); + Assert.AreEqual(0, context.Scopes.Count); + } + + [TestMethod] + public void Create_WithValidOptions_ReturnsValidContext() + { + // Arrange & Act + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + + // Assert + Assert.IsNotNull(context); + Assert.IsNotNull(context.Scopes); + Assert.AreEqual(0, context.Scopes.Count); + } + + [TestMethod] + public void CreateScope_WithValidScopeName_RecordsScopeName() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + const string scopeName = "TestScope"; + + // Act + using (context.CreateScope(scopeName)) + { + // Scope is active + } + + // Assert + Assert.AreEqual(1, context.Scopes.Count); + Assert.AreEqual(scopeName, context.Scopes[0]); + } + + [TestMethod] + public void CreateScope_WithNullScopeName_ThrowsArgumentNullException() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsException(() => + { + using (context.CreateScope(null)) + { + } + }); + + Assert.AreEqual("scope", ex.ParamName); + } + + [TestMethod] + public void CreateScope_WithEmptyScopeName_ThrowsArgumentException() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + + // Act & Assert + ArgumentException ex = Assert.ThrowsException(() => + { + using (context.CreateScope(string.Empty)) + { + } + }); + + Assert.AreEqual("scope", ex.ParamName); + } + + [TestMethod] + public void CreateScope_MultipleScopesSequential_RecordsAllScopesInOrder() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + const string scope1 = "Scope1"; + const string scope2 = "Scope2"; + const string scope3 = "Scope3"; + + // Act + using (context.CreateScope(scope1)) + { + } + using (context.CreateScope(scope2)) + { + } + using (context.CreateScope(scope3)) + { + } + + // Assert + Assert.AreEqual(3, context.Scopes.Count); + Assert.AreEqual(scope1, context.Scopes[0]); + Assert.AreEqual(scope2, context.Scopes[1]); + Assert.AreEqual(scope3, context.Scopes[2]); + } + + [TestMethod] + public void CreateScope_NestedScopes_RecordsInLIFOOrder() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + const string outerScope = "OuterScope"; + const string innerScope1 = "InnerScope1"; + const string innerScope2 = "InnerScope2"; + + // Act + using (context.CreateScope(outerScope)) + { + using (context.CreateScope(innerScope1)) + { + // Inner scope 1 completes first + } + + using (context.CreateScope(innerScope2)) + { + // Inner scope 2 completes second + } + // Outer scope completes last + } + + // Assert + // Scopes should be recorded in the order they complete (LIFO for nested) + Assert.AreEqual(3, context.Scopes.Count); + Assert.AreEqual(innerScope1, context.Scopes[0]); + Assert.AreEqual(innerScope2, context.Scopes[1]); + Assert.AreEqual(outerScope, context.Scopes[2]); + } + + [TestMethod] + public void CreateScope_DeeplyNestedScopes_RecordsAllCorrectly() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + + // Act + using (context.CreateScope("Level1")) + { + using (context.CreateScope("Level2")) + { + using (context.CreateScope("Level3")) + { + using (context.CreateScope("Level4")) + { + // Deepest level + } + } + } + } + + // Assert + Assert.AreEqual(4, context.Scopes.Count); + Assert.AreEqual("Level4", context.Scopes[0]); + Assert.AreEqual("Level3", context.Scopes[1]); + Assert.AreEqual("Level2", context.Scopes[2]); + Assert.AreEqual("Level1", context.Scopes[3]); + } + + [TestMethod] + public void CreateScope_RecordsTimingInformation() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + const string scopeName = "TimedScope"; + const int delayMs = 50; + + // Act + using (context.CreateScope(scopeName)) + { + Thread.Sleep(delayMs); + } + + // Assert + Assert.AreEqual(1, context.Scopes.Count); + Assert.AreEqual(scopeName, context.Scopes[0]); + + // Verify timing was recorded (we can't easily access ScopeRecord directly in this test, + // but the scope name being recorded confirms the timing infrastructure is working) + } + + [TestMethod] + public void CreateScope_WithSameScopeNameMultipleTimes_RecordsEachOccurrence() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + const string scopeName = "RepeatedScope"; + + // Act + using (context.CreateScope(scopeName)) + { + } + using (context.CreateScope(scopeName)) + { + } + using (context.CreateScope(scopeName)) + { + } + + // Assert + Assert.AreEqual(3, context.Scopes.Count); + Assert.IsTrue(context.Scopes.All(s => s == scopeName)); + } + + [TestMethod] + public void Scopes_BeforeAnyScopes_ReturnsEmptyArray() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + + // Act + IReadOnlyList scopes = context.Scopes; + + // Assert + Assert.IsNotNull(scopes); + Assert.AreEqual(0, scopes.Count); + } + + [TestMethod] + public void Scopes_AfterScopesAdded_ReturnsSnapshot() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + using (context.CreateScope("Scope1")) + { + } + + // Act + IReadOnlyList snapshot1 = context.Scopes; + + using (context.CreateScope("Scope2")) + { + } + + IReadOnlyList snapshot2 = context.Scopes; + + // Assert + Assert.AreEqual(1, snapshot1.Count); + Assert.AreEqual(2, snapshot2.Count); + Assert.AreEqual("Scope1", snapshot1[0]); + Assert.AreEqual("Scope1", snapshot2[0]); + Assert.AreEqual("Scope2", snapshot2[1]); + } + + [TestMethod] + public void CreateScope_WithEncryptionPrefixes_BuildsCorrectScopeNames() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + string encryptStreamScope = CosmosDiagnosticsContext.ScopeEncryptModeSelectionPrefix + "Stream"; + string decryptNewtonsoftScope = CosmosDiagnosticsContext.ScopeDecryptModeSelectionPrefix + "Newtonsoft"; + + // Act + using (context.CreateScope(encryptStreamScope)) + { + } + using (context.CreateScope(decryptNewtonsoftScope)) + { + } + + // Assert + Assert.AreEqual(2, context.Scopes.Count); + Assert.AreEqual("EncryptionProcessor.Encrypt.Mde.Stream", context.Scopes[0]); + Assert.AreEqual("EncryptionProcessor.Decrypt.Mde.Newtonsoft", context.Scopes[1]); + } + + [TestMethod] + public void CreateScope_ConcurrentScopesOnDifferentContexts_AreIndependent() + { + // Arrange + TestableCosmosDiagnosticsContext context1 = new TestableCosmosDiagnosticsContext(); + TestableCosmosDiagnosticsContext context2 = new TestableCosmosDiagnosticsContext(); + + // Act + using (context1.CreateScope("Context1Scope1")) + { + } + using (context2.CreateScope("Context2Scope1")) + { + } + using (context1.CreateScope("Context1Scope2")) + { + } + + // Assert + Assert.AreEqual(2, context1.Scopes.Count); + Assert.AreEqual(1, context2.Scopes.Count); + Assert.AreEqual("Context1Scope1", context1.Scopes[0]); + Assert.AreEqual("Context1Scope2", context1.Scopes[1]); + Assert.AreEqual("Context2Scope1", context2.Scopes[0]); + } + + [TestMethod] + public void CreateScope_ThreadSafety_RecordsAllScopesFromConcurrentThreads() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + const int threadCount = 10; + const int scopesPerThread = 5; + CountdownEvent countdown = new CountdownEvent(threadCount); + + // Act + List tasks = new List(); + for (int i = 0; i < threadCount; i++) + { + int threadId = i; + tasks.Add(Task.Run(() => + { + for (int j = 0; j < scopesPerThread; j++) + { + using (context.CreateScope($"Thread{threadId}_Scope{j}")) + { + Thread.Sleep(1); // Small delay to increase interleaving + } + } + countdown.Signal(); + })); + } + + countdown.Wait(); + Task.WaitAll(tasks.ToArray()); + + // Assert + Assert.AreEqual(threadCount * scopesPerThread, context.Scopes.Count); + + // Verify all thread scopes are present + for (int i = 0; i < threadCount; i++) + { + for (int j = 0; j < scopesPerThread; j++) + { + string expectedScope = $"Thread{i}_Scope{j}"; + Assert.IsTrue(context.Scopes.Contains(expectedScope), + $"Expected scope '{expectedScope}' not found in recorded scopes"); + } + } + } + + [TestMethod] + public void Scope_Dispose_IsIdempotent() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + TestableCosmosDiagnosticsContext.TestableScope scope = context.CreateScope("IdempotentScope"); + + // Act - dispose multiple times (this tests the idempotency guarantee) + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + + // Assert - should only record once even though Dispose was called 3 times + Assert.AreEqual(1, context.Scopes.Count); + Assert.AreEqual("IdempotentScope", context.Scopes[0]); + } + + [TestMethod] + public void CreateScope_WithExceptionInScope_StillRecordsTiming() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + const string scopeName = "ExceptionScope"; + + // Act & Assert + try + { + using (context.CreateScope(scopeName)) + { + throw new InvalidOperationException("Test exception"); + } + } + catch (InvalidOperationException) + { + // Expected + } + + // Assert - scope should still be recorded even though exception was thrown + Assert.AreEqual(1, context.Scopes.Count); + Assert.AreEqual(scopeName, context.Scopes[0]); + } + + [TestMethod] + public void CreateScope_AsyncOperations_RecordsCorrectly() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + + // Act + async Task TestAsync() + { + using (context.CreateScope("AsyncScope1")) + { + await Task.Delay(10); + } + + using (context.CreateScope("AsyncScope2")) + { + await Task.Delay(10); + } + } + + TestAsync().GetAwaiter().GetResult(); + + // Assert + Assert.AreEqual(2, context.Scopes.Count); + Assert.AreEqual("AsyncScope1", context.Scopes[0]); + Assert.AreEqual("AsyncScope2", context.Scopes[1]); + } + + [TestMethod] + public void ScopeRecord_Properties_AreCorrect() + { + // Arrange + const string name = "TestScope"; + long startTimestamp = Stopwatch.GetTimestamp(); + long elapsedTicks = Stopwatch.Frequency; + + // Act + TestableCosmosDiagnosticsContext.ScopeRecord record = new TestableCosmosDiagnosticsContext.ScopeRecord( + name, + startTimestamp, + elapsedTicks); + + // Assert + Assert.AreEqual(name, record.Name); + Assert.AreEqual(startTimestamp, record.StartTimestamp); + Assert.AreEqual(elapsedTicks, record.ElapsedTicks); + + // Elapsed should convert ticks to TimeSpan + TimeSpan elapsed = record.Elapsed; + Assert.IsTrue(elapsed.TotalSeconds >= 0.9 && elapsed.TotalSeconds <= 1.1, + $"Expected ~1 second, got {elapsed.TotalSeconds} seconds"); + } + + [TestMethod] + public void CreateScope_LongRunningScope_RecordsAccurateElapsedTime() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + const int delayMs = 100; + + // Act + Stopwatch sw = Stopwatch.StartNew(); + using (context.CreateScope("LongScope")) + { + Thread.Sleep(delayMs); + } + sw.Stop(); + + // Assert + Assert.AreEqual(1, context.Scopes.Count); + // We can verify the scope was recorded, actual timing validation would require + // accessing the internal ScopeRecord which is tested separately + } + + [TestMethod] + public void CreateScope_ScopePrefixes_AreDefinedCorrectly() + { + // Assert + Assert.AreEqual("EncryptionProcessor.Encrypt.Mde.", + CosmosDiagnosticsContext.ScopeEncryptModeSelectionPrefix); + Assert.AreEqual("EncryptionProcessor.Decrypt.Mde.", + CosmosDiagnosticsContext.ScopeDecryptModeSelectionPrefix); + } + + [TestMethod] + public void CreateScope_WithWhitespaceOnlyScopeName_ThrowsArgumentException() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + + // Act & Assert + ArgumentException ex = Assert.ThrowsException(() => + { + using (context.CreateScope(" ")) + { + } + }); + + Assert.AreEqual("scope", ex.ParamName); + } + + [TestMethod] + public void CreateScope_ComplexNestedAndSequentialPattern_RecordsCorrectly() + { + // Arrange + TestableCosmosDiagnosticsContext context = new TestableCosmosDiagnosticsContext(); + + // Act - Complex pattern: Outer { Inner1 { InnerInner1 } Inner2 } Sequential + using (context.CreateScope("Outer")) + { + using (context.CreateScope("Inner1")) + { + using (context.CreateScope("InnerInner1")) + { + } + } + using (context.CreateScope("Inner2")) + { + } + } + using (context.CreateScope("Sequential")) + { + } + + // Assert - Recording order: InnerInner1, Inner1, Inner2, Outer, Sequential + Assert.AreEqual(5, context.Scopes.Count); + Assert.AreEqual("InnerInner1", context.Scopes[0]); + Assert.AreEqual("Inner1", context.Scopes[1]); + Assert.AreEqual("Inner2", context.Scopes[2]); + Assert.AreEqual("Outer", context.Scopes[3]); + Assert.AreEqual("Sequential", context.Scopes[4]); + } + + #region Activity Integration Tests (Production CosmosDiagnosticsContext) + + [TestMethod] + public void ProductionContext_CreateScope_CreatesActivity() + { + // Test the actual production CosmosDiagnosticsContext, not the wrapper + List capturedActivities = new List(); + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => { lock (capturedActivities) { capturedActivities.Add(activity); } } + }; + ActivitySource.AddActivityListener(listener); + + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + using (context.CreateScope("TestScope")) + { + // Activity should be created and started + } + + lock (capturedActivities) + { + Assert.AreEqual(1, capturedActivities.Count, "Should have captured exactly one activity"); + Assert.AreEqual("TestScope", capturedActivities[0].DisplayName); + } + } + + [TestMethod] + public void ProductionContext_NestedScopes_CreatesNestedActivities() + { + List capturedActivities = new List(); + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => { lock (capturedActivities) { capturedActivities.Add(activity); } } + }; + ActivitySource.AddActivityListener(listener); + + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + using (context.CreateScope("Outer")) + { + using (context.CreateScope("Inner")) + { + // Both activities should be created + } + } + + lock (capturedActivities) + { + Assert.AreEqual(2, capturedActivities.Count, "Should have captured two activities"); + Assert.AreEqual("Outer", capturedActivities[0].DisplayName); + Assert.AreEqual("Inner", capturedActivities[1].DisplayName); + + // Inner should be a child of Outer + Assert.AreEqual(capturedActivities[0].Id, capturedActivities[1].ParentId); + } + } + + [TestMethod] + public void ProductionContext_WithoutListener_DoesNotCreateActivity() + { + // When no listener is active, Activity should be null (optimization) + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + + // Create scope without any listener - should work but not create Activity + using (CosmosDiagnosticsContext.Scope scope = context.CreateScope("NoListener")) + { + // Should not throw, even though no Activity is created + } + + // Test passes if no exception is thrown + Assert.IsTrue(true); + } + + [TestMethod] + public void ProductionContext_ScopeDisposal_StopsActivity() + { + Activity startedActivity = null; + Activity stoppedActivity = null; + + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => startedActivity = activity, + ActivityStopped = activity => stoppedActivity = activity + }; + ActivitySource.AddActivityListener(listener); + + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + using (context.CreateScope("TestScope")) + { + Assert.IsNotNull(startedActivity, "Activity should have started"); + Assert.IsNull(stoppedActivity, "Activity should not be stopped yet"); + } + + Assert.IsNotNull(stoppedActivity, "Activity should be stopped after dispose"); + Assert.AreSame(startedActivity, stoppedActivity, "Same activity should be started and stopped"); + } + + [TestMethod] + public void ProductionContext_IdempotentDisposal_OnlyStopsActivityOnce() + { + int stopCount = 0; + + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStopped = activity => stopCount++ + }; + ActivitySource.AddActivityListener(listener); + + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + CosmosDiagnosticsContext.Scope scope = context.CreateScope("TestScope"); + + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + + Assert.AreEqual(1, stopCount, "Activity should only be stopped once despite multiple Dispose calls"); + } + + [TestMethod] + public void ProductionContext_MultipleScopes_EachCreatesOwnActivity() + { + List capturedActivities = new List(); + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => { lock (capturedActivities) { capturedActivities.Add(activity); } } + }; + ActivitySource.AddActivityListener(listener); + + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + + using (context.CreateScope("Scope1")) { } + using (context.CreateScope("Scope2")) { } + using (context.CreateScope("Scope3")) { } + + lock (capturedActivities) + { + Assert.AreEqual(3, capturedActivities.Count); + Assert.AreEqual("Scope1", capturedActivities[0].DisplayName); + Assert.AreEqual("Scope2", capturedActivities[1].DisplayName); + Assert.AreEqual("Scope3", capturedActivities[2].DisplayName); + } + } + + [TestMethod] + public void ProductionContext_ActivityKind_IsInternal() + { + Activity capturedActivity = null; + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => capturedActivity = activity + }; + ActivitySource.AddActivityListener(listener); + + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + using (context.CreateScope("TestScope")) { } + + Assert.IsNotNull(capturedActivity); + Assert.AreEqual(ActivityKind.Internal, capturedActivity.Kind); + } + + [TestMethod] + public void ProductionContext_ActivitySource_HasCorrectName() + { + Activity capturedActivity = null; + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => capturedActivity = activity + }; + ActivitySource.AddActivityListener(listener); + + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + using (context.CreateScope("TestScope")) { } + + Assert.IsNotNull(capturedActivity); + Assert.AreEqual("Microsoft.Azure.Cosmos.Encryption.Custom", capturedActivity.Source.Name); + } + + [TestMethod] + public void ProductionContext_ConcurrentScopes_IndependentActivities() + { + List capturedActivities = new List(); + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => { lock (capturedActivities) { capturedActivities.Add(activity); } } + }; + ActivitySource.AddActivityListener(listener); + + CosmosDiagnosticsContext context1 = CosmosDiagnosticsContext.Create(null); + CosmosDiagnosticsContext context2 = CosmosDiagnosticsContext.Create(null); + + using (context1.CreateScope("Context1Scope")) + using (context2.CreateScope("Context2Scope")) + { + // Both should create independent activities + } + + lock (capturedActivities) + { + Assert.AreEqual(2, capturedActivities.Count); + // They should have different IDs + Assert.AreNotEqual(capturedActivities[0].Id, capturedActivities[1].Id); + } + } + + [TestMethod] + public void ProductionContext_ScopeWithNullName_ThrowsArgumentException() + { + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + + // ArgumentNullException is a subclass of ArgumentException, so we expect ArgumentNullException specifically + ArgumentNullException exception = Assert.ThrowsException(() => + { + using (context.CreateScope(null)) { } + }); + + Assert.IsTrue(exception.ParamName == "scope"); + } + + [TestMethod] + public void ProductionContext_ScopeWithEmptyName_ThrowsArgumentException() + { + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + + ArgumentException exception = Assert.ThrowsException(() => + { + using (context.CreateScope(string.Empty)) { } + }); + + Assert.IsTrue(exception.Message.Contains("scope")); + } + + [TestMethod] + public void ProductionContext_ScopeWithWhitespaceName_ThrowsArgumentException() + { + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + + ArgumentException exception = Assert.ThrowsException(() => + { + using (context.CreateScope(" ")) { } + }); + + Assert.IsTrue(exception.Message.Contains("scope")); + } + + [TestMethod] + public void ProductionContext_MultipleListeners_AllReceiveEvents() + { + int listener1StartCount = 0; + int listener2StartCount = 0; + + using ActivityListener listener1 = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => listener1StartCount++ + }; + + using ActivityListener listener2 = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => listener2StartCount++ + }; + + ActivitySource.AddActivityListener(listener1); + ActivitySource.AddActivityListener(listener2); + + CosmosDiagnosticsContext context = CosmosDiagnosticsContext.Create(null); + using (context.CreateScope("TestScope")) { } + + Assert.AreEqual(1, listener1StartCount, "Listener 1 should receive the event"); + Assert.AreEqual(1, listener2StartCount, "Listener 2 should receive the event"); + } + + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/DataEncryptionKeyContainerCoreTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/DataEncryptionKeyContainerCoreTests.cs new file mode 100644 index 0000000000..bddb163253 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/DataEncryptionKeyContainerCoreTests.cs @@ -0,0 +1,103 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Encryption.Custom; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class DataEncryptionKeyContainerCoreTests + { + private TestEncryptionKeyStoreProvider keyStore; + private CosmosDataEncryptionKeyProvider provider; + private DataEncryptionKeyContainerCore sut; + + [TestInitialize] + public void TestInit() + { + this.keyStore = new TestEncryptionKeyStoreProvider(); + this.provider = new CosmosDataEncryptionKeyProvider(this.keyStore); + this.sut = new DataEncryptionKeyContainerCore(this.provider); + } + + [TestMethod] + public async Task FetchDataEncryptionKeyPropertiesAsync_WithCachedValue_ReturnsFromCache() + { + DataEncryptionKeyProperties dekProperties = CreateMdeDekProperties(); + this.provider.DekCache.SetDekProperties(dekProperties.Id, dekProperties); + + DataEncryptionKeyProperties fetched = await this.sut.FetchDataEncryptionKeyPropertiesAsync( + dekProperties.Id, + CosmosDiagnosticsContext.Create(null), + CancellationToken.None); + + Assert.AreEqual(dekProperties, fetched); + } + + [TestMethod] + public async Task FetchUnwrappedAsync_WithMdeDek_ReturnsInMemoryRawDek() + { + DataEncryptionKeyProperties dekProperties = CreateMdeDekProperties(); + + InMemoryRawDek rawDek = await this.sut.FetchUnwrappedAsync( + dekProperties, + CosmosDiagnosticsContext.Create(null), + CancellationToken.None, + withRawKey: true); + + Assert.IsNotNull(rawDek?.DataEncryptionKey?.RawKey); + Assert.AreEqual(1, this.keyStore.UnwrapCalls); + } + + [TestMethod] + public async Task WrapAsync_WithMdeAlgorithm_RoundTripsKey() + { + byte[] rawKey = this.keyStore.DerivedRawKey; + + (byte[] wrapped, _, InMemoryRawDek rawDek) = await this.sut.WrapAsync( + id: "dekWrap", + key: rawKey, + encryptionAlgorithm: CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + metadata: new EncryptionKeyWrapMetadata("name", "value"), + diagnosticsContext: CosmosDiagnosticsContext.Create(null), + cancellationToken: CancellationToken.None); + + Assert.AreEqual(1, this.keyStore.WrapCalls); + Assert.AreEqual(1, this.keyStore.UnwrapCalls); + Assert.IsNotNull(wrapped); + Assert.IsNull(rawDek); + } + + [TestMethod] + public async Task WrapAsync_WithUnsupportedAlgorithm_ThrowsArgumentException() + { + ArgumentException ex = await Assert.ThrowsExceptionAsync(async () => + await this.sut.WrapAsync( + id: "dekBad", + key: new byte[32], + encryptionAlgorithm: "BadAlgo", + metadata: new EncryptionKeyWrapMetadata("name", "value"), + diagnosticsContext: CosmosDiagnosticsContext.Create(null), + cancellationToken: CancellationToken.None)); + + StringAssert.Contains(ex.Message, "Unsupported encryption algorithm"); + } + + private static DataEncryptionKeyProperties CreateMdeDekProperties(string id = "dek") + { + return new( + id, + CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + wrappedDataEncryptionKey: Enumerable.Range(0, 32).Select(i => (byte)i).ToArray(), + encryptionKeyWrapMetadata: new EncryptionKeyWrapMetadata("name", "value"), + createdTime: DateTime.UtcNow) + { ETag = "etag" }; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionOptionsExtensionsTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionOptionsExtensionsTests.cs index 373a120a60..5a1bb0c9c9 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionOptionsExtensionsTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionOptionsExtensionsTests.cs @@ -16,41 +16,32 @@ public void Validate_EncryptionOptions_Throws() DataEncryptionKeyId = null, EncryptionAlgorithm = "something", PathsToEncrypt = new List() - }.Validate()); + }.Validate(default)); Assert.ThrowsException(() => new EncryptionOptions() { DataEncryptionKeyId = "something", EncryptionAlgorithm = null, PathsToEncrypt = new List() - }.Validate()); + }.Validate(default)); Assert.ThrowsException(() => new EncryptionOptions() { DataEncryptionKeyId = "something", EncryptionAlgorithm = "something", PathsToEncrypt = null - }.Validate()); + }.Validate(default)); - Assert.ThrowsException(() => new EncryptionOptions() +#if NET8_0_OR_GREATER +#pragma warning disable CS0618 // Type or member is obsolete + Assert.ThrowsException(() => new EncryptionOptions() { DataEncryptionKeyId = "something", - EncryptionAlgorithm = "something", - PathsToEncrypt = new List(), - CompressionOptions = new CompressionOptions() - { - MinimalCompressedLength = -1 - } - }.Validate()); - } - - [TestMethod] - public void Validate_CompressionOptions_Throws() - { - Assert.ThrowsException(() => new CompressionOptions() - { - MinimalCompressedLength = -1 - }.Validate()); + EncryptionAlgorithm = CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized, + PathsToEncrypt = new List { "/id" }, + }.Validate(JsonProcessor.Stream)); +#pragma warning restore CS0618 // Type or member is obsolete +#endif } } } diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionProcessorTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionProcessorTests.cs new file mode 100644 index 0000000000..d0154ceef2 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionProcessorTests.cs @@ -0,0 +1,230 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Encryption.Custom; + using Microsoft.Azure.Cosmos.Encryption.Custom.Tests; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Newtonsoft.Json.Linq; + using TestDoc = TestCommon.TestDoc; +#if NET8_0_OR_GREATER + using Microsoft.Azure.Cosmos; +#endif + + [TestClass] + public class EncryptionProcessorTests + { + private static Mock mockEncryptor; + private const string DekId = "dekId"; + + [ClassInitialize] + public static void ClassInitialize(TestContext ctx) + { + _ = ctx; + mockEncryptor = TestEncryptorFactory.CreateMde(DekId, out _); + } + +#if NET8_0_OR_GREATER + private static EncryptionOptions CreateMdeOptions() + { + return new() + { + DataEncryptionKeyId = DekId, +#pragma warning disable CS0618 + EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, +#pragma warning restore CS0618 + PathsToEncrypt = TestDoc.PathsToEncrypt, + }; + } + + [TestMethod] + public async Task EncryptDecrypt_StreamProcessor_WithProvidedOutput() + { + TestDoc doc = TestDoc.Create(); + EncryptionOptions opts = CreateMdeOptions(); + + // Capture activities to validate scopes are created + List capturedActivities = new List(); + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => { lock (capturedActivities) { capturedActivities.Add(activity); } } + }; + ActivitySource.AddActivityListener(listener); + + CosmosDiagnosticsContext diagEncrypt = CosmosDiagnosticsContext.Create(null); + MemoryStream encrypted = new(); + await EncryptionProcessor.EncryptAsync(doc.ToStream(), encrypted, mockEncryptor.Object, opts, JsonProcessor.Stream, diagEncrypt, CancellationToken.None); + encrypted.Position = 0; + + CosmosDiagnosticsContext diagDecrypt = CosmosDiagnosticsContext.Create(null); + MemoryStream decryptedOut = new(); + ItemRequestOptions requestOptions = new() { Properties = new Dictionary { { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, JsonProcessor.Stream } } }; + DecryptionContext ctx = await EncryptionProcessor.DecryptAsync(encrypted, decryptedOut, mockEncryptor.Object, diagDecrypt, requestOptions, CancellationToken.None); + + decryptedOut.Position = 0; + JObject decryptedObj = EncryptionProcessor.BaseSerializer.FromStream(decryptedOut); + Assert.AreEqual(doc.SensitiveStr, decryptedObj.Property(nameof(TestDoc.SensitiveStr)).Value.Value()); + Assert.IsNull(decryptedObj.Property(Constants.EncryptedInfo)); + Assert.IsNotNull(ctx); + Assert.IsTrue(ctx.DecryptionInfoList.First().PathsDecrypted.All(p => TestDoc.PathsToEncrypt.Contains(p))); + + // Validate diagnostic scopes were created + string expectedEncryptScope = CosmosDiagnosticsContext.ScopeEncryptModeSelectionPrefix + JsonProcessor.Stream; + string expectedDecryptScope = CosmosDiagnosticsContext.ScopeDecryptModeSelectionPrefix + JsonProcessor.Stream; + lock (capturedActivities) + { + Assert.IsTrue(capturedActivities.Any(a => a.DisplayName == expectedEncryptScope), + $"Expected encrypt scope '{expectedEncryptScope}' not found. Activities: {string.Join(", ", capturedActivities.Select(a => a.DisplayName))}"); + Assert.IsTrue(capturedActivities.Any(a => a.DisplayName == expectedDecryptScope), + $"Expected decrypt scope '{expectedDecryptScope}' not found. Activities: {string.Join(", ", capturedActivities.Select(a => a.DisplayName))}"); + } + } + + [TestMethod] + public async Task Encrypt_NewtonsoftProcessor_Works() + { + TestDoc doc = TestDoc.Create(); + EncryptionOptions opts = CreateMdeOptions(); + + // Capture activities to validate scopes are created + List capturedActivities = new List(); + using ActivityListener listener = new ActivityListener + { + ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.Azure.Cosmos.Encryption.Custom", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded, + ActivityStarted = activity => { lock (capturedActivities) { capturedActivities.Add(activity); } } + }; + ActivitySource.AddActivityListener(listener); + + CosmosDiagnosticsContext diagEncrypt = CosmosDiagnosticsContext.Create(null); + Stream encrypted = await EncryptionProcessor.EncryptAsync(doc.ToStream(), mockEncryptor.Object, opts, JsonProcessor.Newtonsoft, diagEncrypt, CancellationToken.None); + + Assert.IsNotNull(encrypted); + encrypted.Dispose(); + + // Validate Newtonsoft encrypt scope was created + string expectedEncryptScope = CosmosDiagnosticsContext.ScopeEncryptModeSelectionPrefix + JsonProcessor.Newtonsoft; + lock (capturedActivities) + { + Assert.IsTrue(capturedActivities.Any(a => a.DisplayName == expectedEncryptScope), + $"Expected Newtonsoft encrypt scope '{expectedEncryptScope}' not found. Activities: {string.Join(", ", capturedActivities.Select(a => a.DisplayName))}"); + } + } + + [TestMethod] + public async Task Decrypt_StreamSelection_FallbackWhenUnencrypted() + { + string json = "{\"id\":\"id1\",\"pk\":\"pk1\",\"NonSensitive\":\"v\"}"; // no _ei + MemoryStream input = new(System.Text.Encoding.UTF8.GetBytes(json)); + CosmosDiagnosticsContext ctxDiag = CosmosDiagnosticsContext.Create(null); + ItemRequestOptions opts = new() { Properties = new Dictionary { { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, JsonProcessor.Stream } } }; + (Stream result, DecryptionContext ctxDec) = await EncryptionProcessor.DecryptAsync(input, mockEncryptor.Object, ctxDiag, opts, CancellationToken.None); + Assert.IsNull(ctxDec); + Assert.AreEqual(0, result.Position); + } +#endif + +#if NET8_0_OR_GREATER + [TestMethod] + public async Task Decrypt_StreamSelection_LegacyAlgorithm_FallsBackToNewtonsoft() + { + TestDoc doc = TestDoc.Create(); + EncryptionOptions legacy = new() + { + DataEncryptionKeyId = DekId, +#pragma warning disable CS0618 + EncryptionAlgorithm = CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized, +#pragma warning restore CS0618 + PathsToEncrypt = TestDoc.PathsToEncrypt, + }; + Stream legacyEncrypted = await EncryptionProcessor.EncryptAsync(doc.ToStream(), mockEncryptor.Object, legacy, JsonProcessor.Newtonsoft, CosmosDiagnosticsContext.Create(null), CancellationToken.None); + legacyEncrypted.Position = 0; + + ItemRequestOptions opts = new() { Properties = new Dictionary { { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, "Stream" } } }; + CosmosDiagnosticsContext diag = CosmosDiagnosticsContext.Create(null); + + // Legacy algorithm should decrypt successfully by falling back to the legacy decryption path + (Stream decrypted, DecryptionContext context) = await EncryptionProcessor.DecryptAsync(legacyEncrypted, mockEncryptor.Object, diag, opts, CancellationToken.None); + + Assert.IsNotNull(decrypted); + Assert.IsNotNull(context); + decrypted.Position = 0; + TestDoc result = TestCommon.FromStream(decrypted); + Assert.AreEqual(doc, result); + } + + [TestMethod] + public async Task DecryptProvidedOutput_StreamSelection_LegacyAlgorithm_Throws() + { + TestDoc doc = TestDoc.Create(); + EncryptionOptions legacy = new() + { + DataEncryptionKeyId = DekId, +#pragma warning disable CS0618 + EncryptionAlgorithm = CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized, +#pragma warning restore CS0618 + PathsToEncrypt = TestDoc.PathsToEncrypt, + }; + Stream legacyEncrypted = await EncryptionProcessor.EncryptAsync(doc.ToStream(), mockEncryptor.Object, legacy, JsonProcessor.Newtonsoft, CosmosDiagnosticsContext.Create(null), CancellationToken.None); + legacyEncrypted.Position = 0; + + ItemRequestOptions opts = new() { Properties = new Dictionary { { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, "Stream" } } }; + CosmosDiagnosticsContext diag = CosmosDiagnosticsContext.Create(null); + MemoryStream output = new(); + + NotSupportedException exception = await Assert.ThrowsExceptionAsync(async () => + { + await EncryptionProcessor.DecryptAsync(legacyEncrypted, output, mockEncryptor.Object, diag, opts, CancellationToken.None); + }); + + Assert.IsTrue(exception.Message.Contains("not supported"), $"Unexpected exception message: {exception.Message}"); +#pragma warning disable CS0618 + Assert.IsTrue(exception.Message.Contains(CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized), $"Exception should mention the unsupported algorithm"); +#pragma warning restore CS0618 + } + + [TestMethod] + public async Task Encrypt_LegacyAlgorithm_StreamProcessor_Throws() + { + TestDoc doc = TestDoc.Create(); + EncryptionItemRequestOptions ro = new() + { + EncryptionOptions = new() + { + DataEncryptionKeyId = DekId, +#pragma warning disable CS0618 + EncryptionAlgorithm = CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized, +#pragma warning restore CS0618 + PathsToEncrypt = TestDoc.PathsToEncrypt, + }, + Properties = new Dictionary { { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, "Stream" } } + }; + + CosmosDiagnosticsContext diag = CosmosDiagnosticsContext.Create(null); + + try + { + await EncryptionProcessor.EncryptAsync(doc.ToStream(), mockEncryptor.Object, ro, diag, CancellationToken.None); + Assert.Fail("Expected NotSupportedException for legacy algorithm with Stream processor override."); + } + catch (NotSupportedException ex) + { + Assert.IsTrue(ex.Message.IndexOf("not supported", StringComparison.OrdinalIgnoreCase) >= 0, $"Unexpected message: {ex.Message}"); + } + } +#endif + } +} + diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionPropertiesTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionPropertiesTests.cs index 7d0c2562c2..53e217aed2 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionPropertiesTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/EncryptionPropertiesTests.cs @@ -24,31 +24,6 @@ public void Ctor_AssignsAllMandatoryProperties() Assert.AreEqual("dek-id", properties.DataEncryptionKeyId); Assert.IsTrue(new byte[] { 1, 2, 3, 4, 5}.SequenceEqual(properties.EncryptedData)); Assert.IsTrue(new List { "a", "b"}.SequenceEqual(properties.EncryptedPaths)); - Assert.AreEqual(CompressionOptions.CompressionAlgorithm.None, properties.CompressionAlgorithm); - Assert.IsNull(properties.CompressedEncryptedPaths); } - -#if NET8_0_OR_GREATER - [TestMethod] - public void Ctor_AssignsAllProperties() - { - EncryptionProperties properties = new ( - 11, - "algorithm", - "dek-id", - new byte[] { 1, 2, 3, 4, 5 }, - new List { "a", "b" }, - CompressionOptions.CompressionAlgorithm.Brotli, - new Dictionary { { "a", 246 } }); - - Assert.AreEqual(11, properties.EncryptionFormatVersion); - Assert.AreEqual("algorithm", properties.EncryptionAlgorithm); - Assert.AreEqual("dek-id", properties.DataEncryptionKeyId); - Assert.IsTrue(new byte[] { 1, 2, 3, 4, 5 }.SequenceEqual(properties.EncryptedData)); - Assert.IsTrue(new List { "a", "b" }.SequenceEqual(properties.EncryptedPaths)); - Assert.AreEqual(CompressionOptions.CompressionAlgorithm.Brotli, properties.CompressionAlgorithm); - Assert.IsTrue(new Dictionary { { "a", 246 } }.SequenceEqual(properties.CompressedEncryptedPaths)); - } -#endif } } diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/JsonProcessorRequestOptionsExtensionsTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/JsonProcessorRequestOptionsExtensionsTests.cs new file mode 100644 index 0000000000..aa948cd24c --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/JsonProcessorRequestOptionsExtensionsTests.cs @@ -0,0 +1,131 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +#if NET8_0_OR_GREATER +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Tests +{ + using System; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class JsonProcessorRequestOptionsExtensionsTests + { + [TestMethod] + public void TryReadOverride_EnumValue_Succeeds() + { + RequestOptions ro = new ItemRequestOptions + { + Properties = new Dictionary + { + { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, "Stream" } + } + }; + + bool found = ro.TryReadJsonProcessorOverride(out JsonProcessor jp); + Assert.IsTrue(found); + Assert.AreEqual(JsonProcessor.Stream, jp); + } + + [TestMethod] + public void TryReadOverride_StringValue_CaseInsensitive_Succeeds() + { + RequestOptions ro = new ItemRequestOptions + { + Properties = new Dictionary + { + { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, "sTrEaM" } + } + }; + + bool found = ro.TryReadJsonProcessorOverride(out JsonProcessor jp); + Assert.IsTrue(found); + Assert.AreEqual(JsonProcessor.Stream, jp); + } + + [TestMethod] + public void TryReadOverride_StringValue_ExactMatch_Succeeds() + { + RequestOptions ro = new ItemRequestOptions + { + Properties = new Dictionary + { + { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, "Stream" } + } + }; + + bool found = ro.TryReadJsonProcessorOverride(out JsonProcessor jp); + Assert.IsTrue(found); + Assert.AreEqual(JsonProcessor.Stream, jp); + } + + [TestMethod] + public void TryReadOverride_InvalidString_Ignored() + { + RequestOptions ro = new ItemRequestOptions + { + Properties = new Dictionary + { + { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, "invalid" } + } + }; + + bool found = ro.TryReadJsonProcessorOverride(out JsonProcessor jp); + Assert.IsFalse(found); + Assert.AreEqual(JsonProcessor.Newtonsoft, jp); + } + + [TestMethod] + public void TryReadOverride_NullRequestOptions_Default() + { + RequestOptions roNull = null; + bool found = roNull.TryReadJsonProcessorOverride(out JsonProcessor jp); + Assert.IsFalse(found); + Assert.AreEqual(JsonProcessor.Newtonsoft, jp); + } + + [TestMethod] + public void TryReadOverride_NoPropertiesDictionary_Default() + { + RequestOptions ro = new ItemRequestOptions(); // Properties remains null + bool found = ro.TryReadJsonProcessorOverride(out JsonProcessor jp); + Assert.IsFalse(found); + Assert.AreEqual(JsonProcessor.Newtonsoft, jp); + } + + [TestMethod] + public void TryReadOverride_MixedCaseKey_NotRecognized() + { + RequestOptions ro = new ItemRequestOptions + { + Properties = new Dictionary + { + // Intentionally different casing pattern; should not match for perf (no ToUpper/ToLower) + { "Encryption-Json-Processor", "Stream" } + } + }; + + bool found = ro.TryReadJsonProcessorOverride(out JsonProcessor jp); + Assert.IsFalse(found); + Assert.AreEqual(JsonProcessor.Newtonsoft, jp); + } + + [TestMethod] + public void TryReadOverride_MismatchedKey_NotRecognized() + { + RequestOptions ro = new ItemRequestOptions + { + Properties = new Dictionary + { + { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey + "-extra", "Stream" } + } + }; + + bool found = ro.TryReadJsonProcessorOverride(out JsonProcessor jp); + Assert.IsFalse(found); + Assert.AreEqual(JsonProcessor.Newtonsoft, jp); + } + } +} +#endif diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/LegacyEncryptionProcessorTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/LegacyEncryptionProcessorTests.cs index ecba3413c4..2f805b64fd 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/LegacyEncryptionProcessorTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/LegacyEncryptionProcessorTests.cs @@ -35,18 +35,13 @@ public static void ClassInitialize(TestContext testContext) EncryptionAlgorithm = CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized, PathsToEncrypt = TestDoc.PathsToEncrypt }; - - LegacyEncryptionProcessorTests.mockEncryptor = new Mock(); - LegacyEncryptionProcessorTests.mockEncryptor.Setup(m => m.EncryptAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync((byte[] plainText, string dekId, string algo, CancellationToken t) => - dekId == LegacyEncryptionProcessorTests.dekId ? TestCommon.EncryptData(plainText) : throw new InvalidOperationException("DEK not found.")); - LegacyEncryptionProcessorTests.mockEncryptor.Setup(m => m.DecryptAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync((byte[] cipherText, string dekId, string algo, CancellationToken t) => - dekId == LegacyEncryptionProcessorTests.dekId ? TestCommon.DecryptData(cipherText) : throw new InvalidOperationException("Null DEK was returned.")); + LegacyEncryptionProcessorTests.mockEncryptor = TestEncryptorFactory.CreateLegacy(dekId); } [TestMethod] - public async Task InvalidPathToEncrypt() + [DynamicData(nameof(JsonProcessors))] + + internal async Task InvalidPathToEncrypt(JsonProcessor jsonProcessor) { TestDoc testDoc = TestDoc.Create(); EncryptionOptions encryptionOptionsWithInvalidPathToEncrypt = new () @@ -60,6 +55,7 @@ public async Task InvalidPathToEncrypt() testDoc.ToStream(), LegacyEncryptionProcessorTests.mockEncryptor.Object, encryptionOptionsWithInvalidPathToEncrypt, + jsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); @@ -80,12 +76,13 @@ public async Task InvalidPathToEncrypt() } [TestMethod] - public async Task EncryptDecryptPropertyWithNullValue() + [DynamicData(nameof(JsonProcessors))] + internal async Task EncryptDecryptPropertyWithNullValue(JsonProcessor jsonProcessor) { TestDoc testDoc = TestDoc.Create(); testDoc.SensitiveStr = null; - JObject encryptedDoc = await LegacyEncryptionProcessorTests.VerifyEncryptionSucceeded(testDoc); + JObject encryptedDoc = await LegacyEncryptionProcessorTests.VerifyEncryptionSucceeded(testDoc, jsonProcessor); (JObject decryptedDoc, DecryptionContext decryptionContext) = await EncryptionProcessor.DecryptAsync( encryptedDoc, @@ -101,11 +98,12 @@ public async Task EncryptDecryptPropertyWithNullValue() } [TestMethod] - public async Task ValidateEncryptDecryptDocument() + [DynamicData(nameof(JsonProcessors))] + internal async Task ValidateEncryptDecryptDocument(JsonProcessor jsonProcessor) { TestDoc testDoc = TestDoc.Create(); - JObject encryptedDoc = await LegacyEncryptionProcessorTests.VerifyEncryptionSucceeded(testDoc); + JObject encryptedDoc = await LegacyEncryptionProcessorTests.VerifyEncryptionSucceeded(testDoc, jsonProcessor); (JObject decryptedDoc, DecryptionContext decryptionContext) = await EncryptionProcessor.DecryptAsync( encryptedDoc, @@ -121,7 +119,8 @@ public async Task ValidateEncryptDecryptDocument() } [TestMethod] - public async Task ValidateDecryptStream() + [DynamicData(nameof(JsonProcessors))] + internal async Task ValidateDecryptStream(JsonProcessor jsonProcessor) { TestDoc testDoc = TestDoc.Create(); @@ -129,6 +128,7 @@ public async Task ValidateDecryptStream() testDoc.ToStream(), LegacyEncryptionProcessorTests.mockEncryptor.Object, LegacyEncryptionProcessorTests.encryptionOptions, + jsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); @@ -164,12 +164,13 @@ public async Task DecryptStreamWithoutEncryptedProperty() Assert.IsNull(decryptionContext); } - private static async Task VerifyEncryptionSucceeded(TestDoc testDoc) + private static async Task VerifyEncryptionSucceeded(TestDoc testDoc, JsonProcessor jsonProcessor) { Stream encryptedStream = await EncryptionProcessor.EncryptAsync( testDoc.ToStream(), LegacyEncryptionProcessorTests.mockEncryptor.Object, LegacyEncryptionProcessorTests.encryptionOptions, + jsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); @@ -221,6 +222,17 @@ private static void VerifyDecryptionSucceeded( Assert.IsTrue(TestDoc.PathsToEncrypt.Exists(path => !decryptionInfo.PathsDecrypted.Contains(path))); } } + + public static IEnumerable JsonProcessors + { + get + { + yield return new object[] { JsonProcessor.Newtonsoft }; +#if NET8_0_OR_GREATER + yield return new object[] { JsonProcessor.Stream }; +#endif + } + } } #pragma warning restore CS0618 // Type or member is obsolete diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionAlgorithmTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionAlgorithmTests.cs new file mode 100644 index 0000000000..5fbeead6dc --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionAlgorithmTests.cs @@ -0,0 +1,153 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Encryption.Custom; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MdeEncryptionAlgorithmTests + { + private readonly TestEncryptionKeyStoreProvider encryptionKeyStoreProvider = new (); + private readonly DataEncryptionKeyProperties dekProperties = CreateDekProperties(); + + [TestMethod] + public async Task CreateAsync_WithRawKeyFalse_NullTtl_EncryptDecrypt() + { + MdeEncryptionAlgorithm algorithm = await MdeEncryptionAlgorithm.CreateAsync( + this.dekProperties, + Data.Encryption.Cryptography.EncryptionType.Randomized, + this.encryptionKeyStoreProvider, + cacheTimeToLive: null, + withRawKey: false, + cancellationToken: default); + + ValidateEncryptDecryptRoundTrip(algorithm); + + Assert.IsNull(algorithm.RawKey); + Assert.AreEqual(1, this.encryptionKeyStoreProvider.UnwrapCalls); + } + + [TestMethod] + public async Task CreateAsync_WithRawKeyFalse_ZeroTtl_EncryptDecrypt() + { + MdeEncryptionAlgorithm algorithm = await MdeEncryptionAlgorithm.CreateAsync( + this.dekProperties, + Data.Encryption.Cryptography.EncryptionType.Randomized, + this.encryptionKeyStoreProvider, + cacheTimeToLive: TimeSpan.Zero, + withRawKey: false, + cancellationToken: default); + + ValidateEncryptDecryptRoundTrip(algorithm); + + Assert.IsNull(algorithm.RawKey); + Assert.AreEqual(1, this.encryptionKeyStoreProvider.UnwrapCalls); + } + + [TestMethod] + public async Task CreateAsync_WithRawKeyTrue_NullTtl_RawKeyExposed() + { + MdeEncryptionAlgorithm algorithm = await MdeEncryptionAlgorithm.CreateAsync( + this.dekProperties, + Data.Encryption.Cryptography.EncryptionType.Randomized, + this.encryptionKeyStoreProvider, + cacheTimeToLive: null, + withRawKey: true, + cancellationToken: default); + + ValidateEncryptDecryptRoundTrip(algorithm); + + Assert.IsNotNull(algorithm.RawKey); + Assert.AreEqual(this.encryptionKeyStoreProvider.DerivedRawKey.SequenceEqual(algorithm.RawKey), true); + Assert.AreEqual(1, this.encryptionKeyStoreProvider.UnwrapCalls); + } + + [TestMethod] + public async Task CreateAsync_WithRawKeyTrue_ZeroTtl_RawKeyExposed() + { + MdeEncryptionAlgorithm algorithm = await MdeEncryptionAlgorithm.CreateAsync( + this.dekProperties, + Data.Encryption.Cryptography.EncryptionType.Randomized, + this.encryptionKeyStoreProvider, + cacheTimeToLive: TimeSpan.Zero, + withRawKey: true, + cancellationToken: default); + + ValidateEncryptDecryptRoundTrip(algorithm); + + Assert.IsNotNull(algorithm.RawKey); + Assert.IsTrue(this.encryptionKeyStoreProvider.DerivedRawKey.SequenceEqual(algorithm.RawKey)); + Assert.AreEqual(1, this.encryptionKeyStoreProvider.UnwrapCalls); + } + + [TestMethod] + public async Task CreateAsync_NullDekProperties_Throws() + { + await Assert.ThrowsExceptionAsync(async () => + await MdeEncryptionAlgorithm.CreateAsync( + dekProperties: null, + encryptionType: Data.Encryption.Cryptography.EncryptionType.Randomized, + encryptionKeyStoreProvider: this.encryptionKeyStoreProvider, + cacheTimeToLive: null, + withRawKey: false, + cancellationToken: default)); + } + + [TestMethod] + public async Task CreateAsync_NullProvider_Throws() + { + await Assert.ThrowsExceptionAsync(async () => + await MdeEncryptionAlgorithm.CreateAsync( + dekProperties: this.dekProperties, + encryptionType: Data.Encryption.Cryptography.EncryptionType.Randomized, + encryptionKeyStoreProvider: null, + cacheTimeToLive: null, + withRawKey: false, + cancellationToken: default)); + } + + private static void ValidateEncryptDecryptRoundTrip(MdeEncryptionAlgorithm algorithm) + { + byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 }; + ValidateEncryptDecryptAlloc(algorithm, plaintext); + ValidateEncryptDecryptNonAlloc(algorithm, plaintext); + } + + private static void ValidateEncryptDecryptAlloc(MdeEncryptionAlgorithm algorithm, byte[] plaintext) + { + byte[] ciphertext = algorithm.EncryptData(plaintext); + byte[] decrypted = algorithm.DecryptData(ciphertext); + CollectionAssert.AreNotEqual(plaintext, ciphertext); + CollectionAssert.AreEqual(plaintext, decrypted); + } + + private static void ValidateEncryptDecryptNonAlloc(MdeEncryptionAlgorithm algorithm, byte[] plaintext) + { + int encryptSize = algorithm.GetEncryptByteCount(plaintext.Length); + byte[] ciphertextBuffer = new byte[encryptSize]; + algorithm.EncryptData(plaintext, 0, plaintext.Length, ciphertextBuffer, 0); + + int decryptSize = algorithm.GetDecryptByteCount(ciphertextBuffer.Length); + byte[] plaintextBuffer = new byte[decryptSize]; + algorithm.DecryptData(ciphertextBuffer, 0, ciphertextBuffer.Length, plaintextBuffer, 0); + + CollectionAssert.AreEqual(plaintext, plaintextBuffer.AsSpan(0, plaintext.Length).ToArray()); + } + + private static DataEncryptionKeyProperties CreateDekProperties() + { + return new DataEncryptionKeyProperties( + id: "dek1", + encryptionAlgorithm: CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + wrappedDataEncryptionKey: Enumerable.Range(0, 32).Select(i => (byte)i).ToArray(), + encryptionKeyWrapMetadata: new EncryptionKeyWrapMetadata("name", "value"), + createdTime: DateTime.UtcNow); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionProcessorTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionProcessorTests.cs index 46fde1350c..4a69e0c6ad 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionProcessorTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeEncryptionProcessorTests.cs @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.Encryption.Tests using System; using System.Collections.Generic; using System.IO; - using System.IO.Compression; using System.Linq; #if NET8_0_OR_GREATER using System.Text.Json.Nodes; @@ -15,6 +14,7 @@ namespace Microsoft.Azure.Cosmos.Encryption.Tests using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Encryption.Custom; + using Microsoft.Azure.Cosmos.Encryption.Custom.Tests; #if NET8_0_OR_GREATER using Microsoft.Azure.Cosmos.Encryption.Custom.Transformation; #endif @@ -34,57 +34,31 @@ public static void ClassInitialize(TestContext testContext) { _ = testContext; -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +#if NET8_0_OR_GREATER StreamProcessor.InitialBufferSize = 16; //we force smallest possible initial buffer to make sure both secondary reads and resize paths are executed #endif - Mock DekMock = new(); - DekMock.Setup(m => m.EncryptData(It.IsAny())) - .Returns((byte[] plainText) => TestCommon.EncryptData(plainText)); - DekMock.Setup(m => m.GetEncryptByteCount(It.IsAny())) - .Returns((int plainTextLength) => plainTextLength); - DekMock.Setup(m => m.EncryptData(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((byte[] plainText, int plainTextOffset, int plainTextLength, byte[] output, int outputOffset) => TestCommon.EncryptData(plainText, plainTextOffset, plainTextLength, output, outputOffset)); - DekMock.Setup(m => m.DecryptData(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((byte[] plainText, int plainTextOffset, int plainTextLength, byte[] output, int outputOffset) => TestCommon.DecryptData(plainText, plainTextOffset, plainTextLength, output, outputOffset)); - DekMock.Setup(m => m.GetDecryptByteCount(It.IsAny())) - .Returns((int cipherTextLength) => cipherTextLength); - - - mockEncryptor = new Mock(); - mockEncryptor.Setup(m => m.GetEncryptionKeyAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync((string dekId, string algorithm, CancellationToken token) => - dekId == MdeEncryptionProcessorTests.dekId ? DekMock.Object : throw new InvalidOperationException("DEK not found.")); - - mockEncryptor.Setup(m => m.EncryptAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync((byte[] plainText, string dekId, string algo, CancellationToken t) => - dekId == MdeEncryptionProcessorTests.dekId ? TestCommon.EncryptData(plainText) : throw new InvalidOperationException("DEK not found.")); - - mockEncryptor.Setup(m => m.DecryptAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync((byte[] cipherText, string dekId, string algo, CancellationToken t) => - dekId == MdeEncryptionProcessorTests.dekId ? TestCommon.DecryptData(cipherText) : throw new InvalidOperationException("Null DEK was returned.")); + mockEncryptor = TestEncryptorFactory.CreateMde(dekId, out _); } [TestMethod] - [DataRow(JsonProcessor.Newtonsoft)] -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER - [DataRow(JsonProcessor.Stream)] -#endif - public async Task InvalidPathToEncrypt(JsonProcessor jsonProcessor) + [DynamicData(nameof(JsonProcessors))] + public async Task InvalidPathToEncrypt(int jsonProcessorValue) { + JsonProcessor jsonProcessor = ResolveJsonProcessor(jsonProcessorValue); TestDoc testDoc = TestDoc.Create(); EncryptionOptions encryptionOptionsWithInvalidPathToEncrypt = new() { DataEncryptionKeyId = dekId, EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, PathsToEncrypt = new List() { "/SensitiveStr", "/Invalid" }, - JsonProcessor = jsonProcessor, }; Stream encryptedStream = await EncryptionProcessor.EncryptAsync( testDoc.ToStream(), mockEncryptor.Object, encryptionOptionsWithInvalidPathToEncrypt, + jsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); @@ -106,19 +80,16 @@ public async Task InvalidPathToEncrypt(JsonProcessor jsonProcessor) } [TestMethod] - [DataRow(JsonProcessor.Newtonsoft)] -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER - [DataRow(JsonProcessor.Stream)] -#endif - public async Task DuplicatePathToEncrypt(JsonProcessor jsonProcessor) + [DynamicData(nameof(JsonProcessors))] + public async Task DuplicatePathToEncrypt(int jsonProcessorValue) { + JsonProcessor jsonProcessor = ResolveJsonProcessor(jsonProcessorValue); TestDoc testDoc = TestDoc.Create(); EncryptionOptions encryptionOptionsWithDuplicatePathToEncrypt = new() { DataEncryptionKeyId = dekId, EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, PathsToEncrypt = new List() { "/SensitiveStr", "/SensitiveStr" }, - JsonProcessor = jsonProcessor, }; try @@ -127,6 +98,7 @@ await EncryptionProcessor.EncryptAsync( testDoc.ToStream(), mockEncryptor.Object, encryptionOptionsWithDuplicatePathToEncrypt, + jsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); @@ -139,13 +111,15 @@ await EncryptionProcessor.EncryptAsync( } [TestMethod] - [DynamicData(nameof(EncryptionOptionsCombinations))] - public async Task EncryptDecryptPropertyWithNullValue_VerifyByNewtonsoft(EncryptionOptions encryptionOptions) + [DynamicData(nameof(JsonProcessors))] + public async Task EncryptDecryptPropertyWithNullValue_VerifyByNewtonsoft(int jsonProcessorValue) { + JsonProcessor jsonProcessor = ResolveJsonProcessor(jsonProcessorValue); + EncryptionOptions encryptionOptions = this.CreateEncryptionOptions(); TestDoc testDoc = TestDoc.Create(); testDoc.SensitiveStr = null; - JObject encryptedDoc = await VerifyEncryptionSucceededNewtonsoft(testDoc, encryptionOptions); + JObject encryptedDoc = await VerifyEncryptionSucceededNewtonsoft(testDoc, encryptionOptions, jsonProcessor); (JObject decryptedDoc, DecryptionContext decryptionContext) = await EncryptionProcessor.DecryptAsync( encryptedDoc, @@ -161,12 +135,14 @@ public async Task EncryptDecryptPropertyWithNullValue_VerifyByNewtonsoft(Encrypt } [TestMethod] - [DynamicData(nameof(EncryptionOptionsCombinations))] - public async Task ValidateEncryptDecryptDocument_VerifyByNewtonsoft(EncryptionOptions encryptionOptions) + [DynamicData(nameof(JsonProcessors))] + public async Task ValidateEncryptDecryptDocument_VerifyByNewtonsoft(int jsonProcessorValue) { + JsonProcessor jsonProcessor = ResolveJsonProcessor(jsonProcessorValue); + EncryptionOptions encryptionOptions = this.CreateEncryptionOptions(); TestDoc testDoc = TestDoc.Create(); - JObject encryptedDoc = await VerifyEncryptionSucceededNewtonsoft(testDoc, encryptionOptions); + JObject encryptedDoc = await VerifyEncryptionSucceededNewtonsoft(testDoc, encryptionOptions, jsonProcessor); (JObject decryptedDoc, DecryptionContext decryptionContext) = await EncryptionProcessor.DecryptAsync( encryptedDoc, @@ -182,15 +158,18 @@ public async Task ValidateEncryptDecryptDocument_VerifyByNewtonsoft(EncryptionOp } [TestMethod] - [DynamicData(nameof(EncryptionOptionsCombinations))] - public async Task ValidateDecryptByNewtonsoftStream_VerifyByNewtonsoft(EncryptionOptions encryptionOptions) + [DynamicData(nameof(JsonProcessors))] + public async Task ValidateDecryptByNewtonsoftStream_VerifyByNewtonsoft(int jsonProcessorValue) { + JsonProcessor jsonProcessor = ResolveJsonProcessor(jsonProcessorValue); + EncryptionOptions encryptionOptions = this.CreateEncryptionOptions(); TestDoc testDoc = TestDoc.Create(); Stream encryptedStream = await EncryptionProcessor.EncryptAsync( testDoc.ToStream(), mockEncryptor.Object, encryptionOptions, + jsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); @@ -198,7 +177,7 @@ public async Task ValidateDecryptByNewtonsoftStream_VerifyByNewtonsoft(Encryptio encryptedStream, mockEncryptor.Object, new CosmosDiagnosticsContext(), - JsonProcessor.Newtonsoft, + requestOptions: null, CancellationToken.None); JObject decryptedDoc = EncryptionProcessor.BaseSerializer.FromStream(decryptedStream); @@ -210,15 +189,19 @@ public async Task ValidateDecryptByNewtonsoftStream_VerifyByNewtonsoft(Encryptio } [TestMethod] - [DynamicData(nameof(EncryptionOptionsStreamTestCombinations))] - public async Task ValidateDecryptBySystemTextStream_VerifyByNewtonsoft(EncryptionOptions encryptionOptions, JsonProcessor decryptionJsonProcessor) + [DynamicData(nameof(JsonProcessorCombinations))] + public async Task ValidateDecryptBySystemTextStream_VerifyByNewtonsoft(int encryptionJsonProcessorValue, int decryptionJsonProcessorValue) { + JsonProcessor encryptionJsonProcessor = ResolveJsonProcessor(encryptionJsonProcessorValue); + JsonProcessor decryptionJsonProcessor = ResolveJsonProcessor(decryptionJsonProcessorValue); + EncryptionOptions encryptionOptions = this.CreateEncryptionOptions(); TestDoc testDoc = TestDoc.Create(); Stream encryptedStream = await EncryptionProcessor.EncryptAsync( testDoc.ToStream(), mockEncryptor.Object, encryptionOptions, + encryptionJsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); @@ -226,7 +209,7 @@ public async Task ValidateDecryptBySystemTextStream_VerifyByNewtonsoft(Encryptio encryptedStream, mockEncryptor.Object, new CosmosDiagnosticsContext(), - decryptionJsonProcessor, + RequestOptionsOverrideHelper.Create(decryptionJsonProcessor), CancellationToken.None); JObject decryptedDoc = EncryptionProcessor.BaseSerializer.FromStream(decryptedStream); @@ -237,17 +220,21 @@ public async Task ValidateDecryptBySystemTextStream_VerifyByNewtonsoft(Encryptio decryptionContext); } -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +#if NET8_0_OR_GREATER [TestMethod] - [DynamicData(nameof(EncryptionOptionsStreamTestCombinations))] - public async Task ValidateDecryptBySystemTextStream_VerifyBySystemText(EncryptionOptions encryptionOptions, JsonProcessor decryptionJsonProcessor) + [DynamicData(nameof(JsonProcessorCombinations))] + public async Task ValidateDecryptBySystemTextStream_VerifyBySystemText(int encryptionJsonProcessorValue, int decryptionJsonProcessorValue) { + JsonProcessor encryptionJsonProcessor = ResolveJsonProcessor(encryptionJsonProcessorValue); + JsonProcessor decryptionJsonProcessor = ResolveJsonProcessor(decryptionJsonProcessorValue); + EncryptionOptions encryptionOptions = this.CreateEncryptionOptions(); TestDoc testDoc = TestDoc.Create(); Stream encryptedStream = await EncryptionProcessor.EncryptAsync( testDoc.ToStream(), mockEncryptor.Object, encryptionOptions, + encryptionJsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); @@ -255,7 +242,7 @@ public async Task ValidateDecryptBySystemTextStream_VerifyBySystemText(Encryptio encryptedStream, mockEncryptor.Object, new CosmosDiagnosticsContext(), - decryptionJsonProcessor, + RequestOptionsOverrideHelper.Create(decryptionJsonProcessor), CancellationToken.None); JsonNode decryptedDoc = JsonNode.Parse(decryptedStream); @@ -268,12 +255,10 @@ public async Task ValidateDecryptBySystemTextStream_VerifyBySystemText(Encryptio #endif [TestMethod] - [DataRow(JsonProcessor.Newtonsoft)] -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER - [DataRow(JsonProcessor.Stream)] -#endif - public async Task DecryptStreamWithoutEncryptedProperty(JsonProcessor processor) + [DynamicData(nameof(JsonProcessors))] + public async Task DecryptStreamWithoutEncryptedProperty(int processorValue) { + JsonProcessor processor = ResolveJsonProcessor(processorValue); TestDoc testDoc = TestDoc.Create(); Stream docStream = testDoc.ToStream(); @@ -281,7 +266,7 @@ public async Task DecryptStreamWithoutEncryptedProperty(JsonProcessor processor) docStream, mockEncryptor.Object, new CosmosDiagnosticsContext(), - processor, + RequestOptionsOverrideHelper.Create(processor), CancellationToken.None); Assert.IsTrue(decryptedStream.CanSeek); @@ -290,12 +275,13 @@ public async Task DecryptStreamWithoutEncryptedProperty(JsonProcessor processor) Assert.IsNull(decryptionContext); } - private static async Task VerifyEncryptionSucceededNewtonsoft(TestDoc testDoc, EncryptionOptions encryptionOptions) + private static async Task VerifyEncryptionSucceededNewtonsoft(TestDoc testDoc, EncryptionOptions encryptionOptions, JsonProcessor jsonProcessor) { Stream encryptedStream = await EncryptionProcessor.EncryptAsync( testDoc.ToStream(), mockEncryptor.Object, encryptionOptions, + jsonProcessor, new CosmosDiagnosticsContext(), CancellationToken.None); @@ -316,10 +302,7 @@ private static async Task VerifyEncryptionSucceededNewtonsoft(TestDoc t Assert.IsNotNull(encryptionProperties); Assert.AreEqual(dekId, encryptionProperties.DataEncryptionKeyId); - int expectedVersion = - (encryptionOptions.CompressionOptions.Algorithm != CompressionOptions.CompressionAlgorithm.None) - ? 4 : 3; - Assert.AreEqual(expectedVersion, encryptionProperties.EncryptionFormatVersion); + Assert.AreEqual(EncryptionFormatVersion.Mde, encryptionProperties.EncryptionFormatVersion); Assert.IsNull(encryptionProperties.EncryptedData); Assert.IsNotNull(encryptionProperties.EncryptedPaths); @@ -374,7 +357,7 @@ private static void VerifyDecryptionSucceeded( } } -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER +#if NET8_0_OR_GREATER private static void VerifyDecryptionSucceeded( JsonNode decryptedDoc, TestDoc expectedDoc, @@ -424,45 +407,58 @@ private static void AssertNullableValueKind(T expectedValue, JsonNode node, s } #endif - private static EncryptionOptions CreateEncryptionOptions(JsonProcessor processor, CompressionOptions.CompressionAlgorithm compressionAlgorithm, CompressionLevel compressionLevel) + private EncryptionOptions CreateEncryptionOptions() { return new EncryptionOptions() { DataEncryptionKeyId = dekId, EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, PathsToEncrypt = TestDoc.PathsToEncrypt, - JsonProcessor = processor, - CompressionOptions = new CompressionOptions() - { - Algorithm = compressionAlgorithm, - CompressionLevel = compressionLevel - } }; } - public static IEnumerable EncryptionOptionsCombinations => new[] { - new object[] { CreateEncryptionOptions(JsonProcessor.Newtonsoft, CompressionOptions.CompressionAlgorithm.None, CompressionLevel.NoCompression) }, -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER - new object[] { CreateEncryptionOptions(JsonProcessor.Stream, CompressionOptions.CompressionAlgorithm.None, CompressionLevel.NoCompression) }, - new object[] { CreateEncryptionOptions(JsonProcessor.Newtonsoft, CompressionOptions.CompressionAlgorithm.Brotli, CompressionLevel.Fastest) }, - new object[] { CreateEncryptionOptions(JsonProcessor.Stream, CompressionOptions.CompressionAlgorithm.Brotli, CompressionLevel.Fastest) }, - new object[] { CreateEncryptionOptions(JsonProcessor.Newtonsoft, CompressionOptions.CompressionAlgorithm.Brotli, CompressionLevel.NoCompression) }, - new object[] { CreateEncryptionOptions(JsonProcessor.Stream, CompressionOptions.CompressionAlgorithm.Brotli, CompressionLevel.NoCompression) }, -#endif - }; + public static IEnumerable JsonProcessors + { + get + { + foreach (JsonProcessor processor in EnumerateJsonProcessors()) + { + yield return new object[] { (int)processor }; + } + } + } - public static IEnumerable EncryptionOptionsStreamTestCombinations + public static IEnumerable JsonProcessorCombinations { get { - foreach (object[] encryptionOptions in EncryptionOptionsCombinations) + JsonProcessor[] processors = EnumerateJsonProcessors().ToArray(); + foreach (JsonProcessor encProcessor in processors) { - yield return new object[] { encryptionOptions[0], JsonProcessor.Newtonsoft }; -#if ENCRYPTION_CUSTOM_PREVIEW && NET8_0_OR_GREATER - yield return new object[] { encryptionOptions[0], JsonProcessor.Stream }; -#endif + foreach (JsonProcessor decProcessor in processors) + { + yield return new object[] { (int)encProcessor, (int)decProcessor }; + } } } } + + private static JsonProcessor ResolveJsonProcessor(int value) + { + if (!Enum.IsDefined(typeof(JsonProcessor), value)) + { + throw new ArgumentOutOfRangeException(nameof(value), "Invalid JsonProcessor value supplied to test."); + } + + return (JsonProcessor)value; + } + + private static IEnumerable EnumerateJsonProcessors() + { + yield return JsonProcessor.Newtonsoft; +#if NET8_0_OR_GREATER + yield return JsonProcessor.Stream; +#endif + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeKeyWrapProviderTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeKeyWrapProviderTests.cs new file mode 100644 index 0000000000..e80214ca7d --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/MdeKeyWrapProviderTests.cs @@ -0,0 +1,74 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Encryption.Custom; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MdeKeyWrapProviderTests + { + private TestEncryptionKeyStoreProvider provider; + private MdeKeyWrapProvider keyWrapProvider; + + [TestInitialize] + public void TestInitialize() + { + this.provider = new TestEncryptionKeyStoreProvider(); + this.keyWrapProvider = new MdeKeyWrapProvider(this.provider); + } + + [TestMethod] + public void Constructor_WithNullProvider_ThrowsArgumentNullException() + { + Assert.ThrowsException(() => + { + _ = new MdeKeyWrapProvider(null); + }); + } + + [TestMethod] + public async Task WrapKeyAsync_WithValidInputs_EncryptsUsingUnderlyingProvider() + { + byte[] keyToWrap = Enumerable.Range(0, 32).Select(i => (byte)i).ToArray(); + EncryptionKeyWrapMetadata metadata = new("name", "value"); + + EncryptionKeyWrapResult result = await this.keyWrapProvider.WrapKeyAsync(keyToWrap, metadata, CancellationToken.None); + + Assert.AreEqual(1, this.provider.WrapCalls); + Assert.AreEqual(metadata, result.EncryptionKeyWrapMetadata); + } + + [TestMethod] + public async Task UnwrapKeyAsync_WithValidInputs_DecryptsUsingUnderlyingProvider() + { + byte[] wrappedKey = Enumerable.Range(0, 32).Select(i => (byte)i).ToArray(); + EncryptionKeyWrapMetadata metadata = new("name", "value"); + + EncryptionKeyUnwrapResult result = await this.keyWrapProvider.UnwrapKeyAsync(wrappedKey, metadata, CancellationToken.None); + + Assert.IsNotNull(result?.DataEncryptionKey); + Assert.AreEqual(1, this.provider.UnwrapCalls); + } + + [TestMethod] + public async Task WrapKeyAsync_WithNullMetadata_ThrowsArgumentNullException() + { + await Assert.ThrowsExceptionAsync(async () => + await this.keyWrapProvider.WrapKeyAsync(new byte[32], metadata: null, CancellationToken.None)); + } + + [TestMethod] + public async Task UnwrapKeyAsync_WithNullMetadata_ThrowsArgumentNullException() + { + await Assert.ThrowsExceptionAsync(async () => + await this.keyWrapProvider.UnwrapKeyAsync(new byte[32], metadata: null, CancellationToken.None)); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests.csproj b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests.csproj index 55f611c774..349d0d1095 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests.csproj +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests.csproj @@ -9,11 +9,11 @@ false Microsoft.Azure.Cosmos.Encryption.Tests $(LangVersion) - $(DefineConstants);ENCRYPTION_CUSTOM_PREVIEW + @@ -22,10 +22,12 @@ + + - + Always diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/RequestOptionsOverrideHelper.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/RequestOptionsOverrideHelper.cs new file mode 100644 index 0000000000..9c74c88d33 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/RequestOptionsOverrideHelper.cs @@ -0,0 +1,34 @@ +// Test-only helper to create RequestOptions with json processor override. +// Centralizes the property bag key usage to avoid duplication across tests. +#nullable enable +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Tests +{ + using System.Collections.Generic; + using Microsoft.Azure.Cosmos; + using Microsoft.Azure.Cosmos.Encryption.Custom; + + internal static class RequestOptionsOverrideHelper + { + internal static RequestOptions? Create(JsonProcessor processor) + { +#if NET8_0_OR_GREATER + if (processor == JsonProcessor.Newtonsoft) + { + return null; // default path + } + + // Inline the logic from EncryptionRequestOptionsExperimental.CreateRequestOptions() + ItemRequestOptions requestOptions = new ItemRequestOptions + { + Properties = new Dictionary + { + { JsonProcessorRequestOptionsExtensions.JsonProcessorPropertyBagKey, processor.ToString() } + } + }; + return requestOptions; +#else + return null; +#endif + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/StreamProcessorConcurrencyAndCancellationTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/StreamProcessorConcurrencyAndCancellationTests.cs new file mode 100644 index 0000000000..69808bc0a3 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/StreamProcessorConcurrencyAndCancellationTests.cs @@ -0,0 +1,248 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +#if NET8_0_OR_GREATER +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Tests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.IO.Compression; + using System.Linq; + using System.Text; + using System.Text.Json; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Encryption.Custom; + using Microsoft.Azure.Cosmos.Encryption.Tests; // TestEncryptorFactory & TestCommon + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + + /// + /// Additional coverage for scenarios explicitly requested: large payloads, corrupted payload (assert existing coverage), concurrency and cancellation. + /// Corrupted payload scenarios are already extensively covered in StreamProcessorDecryptorTests; here we focus on large payload and multi-task safety plus cancellation. + /// + [TestClass] + public class StreamProcessorConcurrencyAndCancellationTests + { + private const string DekId = "dekId"; + private static Mock mockEncryptor; + + [ClassInitialize] + public static void Init(TestContext ctx) + { + _ = ctx; + mockEncryptor = TestEncryptorFactory.CreateMde(DekId, out _); + } + + private static EncryptionOptions CreateEncryptionOptions(IEnumerable paths) + { + return new EncryptionOptions + { + DataEncryptionKeyId = DekId, + EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + PathsToEncrypt = paths.ToList() + }; + } + + [TestMethod] + public async Task EncryptDecrypt_LargePayload_StreamProcessor() + { + // Force small initial buffer to exercise multiple resizes while handling a large single property. + int original = Transformation.StreamProcessor.InitialBufferSize; + Transformation.StreamProcessor.InitialBufferSize = 32; // tiny to trigger many growths + try + { + string largeValue = new string('x', 250_000); // ~250 KB + var doc = new + { + id = Guid.NewGuid().ToString(), + Large = largeValue, + P1 = new string('a', 1024), + P2 = "b", + }; + EncryptionOptions options = CreateEncryptionOptions(new[] { "/Large", "/P1", "/P2" }); + + Stream encrypted = await EncryptionProcessor.EncryptAsync( + TestCommon.ToStream(doc), + mockEncryptor.Object, + options, + JsonProcessor.Stream, + new CosmosDiagnosticsContext(), + CancellationToken.None); + + // Decrypt using streaming path explicitly. + (Stream decryptedStream, DecryptionContext ctx) = await EncryptionProcessor.DecryptAsync( + encrypted, + mockEncryptor.Object, + new CosmosDiagnosticsContext(), + RequestOptionsOverrideHelper.Create(JsonProcessor.Stream), + CancellationToken.None); + + Assert.IsNotNull(ctx); + Assert.IsTrue(ctx.DecryptionInfoList[0].PathsDecrypted.Contains("/Large")); + + decryptedStream.Position = 0; + using JsonDocument jd = JsonDocument.Parse(decryptedStream); + JsonElement root = jd.RootElement; + Assert.AreEqual(largeValue.Length, root.GetProperty("Large").GetString().Length); + Assert.AreEqual(1024, root.GetProperty("P1").GetString().Length); + Assert.AreEqual("b", root.GetProperty("P2").GetString()); + } + finally + { + Transformation.StreamProcessor.InitialBufferSize = original; + } + } + + [TestMethod] + public async Task Concurrent_EncryptDecrypt_StreamProcessor() + { + int parallelism = Math.Min(8, Environment.ProcessorCount); + var tasks = new List(); + for (int i = 0; i < parallelism; i++) + { + tasks.Add(Task.Run(async () => + { + string large = new string((char)('a' + (i % 26)), 50_000); + var doc = new { id = Guid.NewGuid().ToString(), Large = large, P1 = i.ToString(), P2 = (i * 2).ToString() }; + EncryptionOptions options = CreateEncryptionOptions(new[] { "/Large", "/P1", "/P2" }); + Stream encrypted = await EncryptionProcessor.EncryptAsync(TestCommon.ToStream(doc), mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + (Stream decrypted, DecryptionContext ctx) = await EncryptionProcessor.DecryptAsync(encrypted, mockEncryptor.Object, new CosmosDiagnosticsContext(), RequestOptionsOverrideHelper.Create(JsonProcessor.Stream), CancellationToken.None); + decrypted.Position = 0; + using JsonDocument jd = JsonDocument.Parse(decrypted); + JsonElement root = jd.RootElement; + Assert.AreEqual(large.Length, root.GetProperty("Large").GetString().Length); + Assert.AreEqual(doc.P1, root.GetProperty("P1").GetString()); + Assert.AreEqual(doc.P2, root.GetProperty("P2").GetString()); + Assert.IsTrue(ctx.DecryptionInfoList[0].PathsDecrypted.Count >= 3); + })); + } + + await Task.WhenAll(tasks); + } + + [TestMethod] + public async Task Encrypt_Cancellation_Aborts() + { + string large = new string('z', 50_000); // forces multiple reads with small chunk size + var doc = new { id = Guid.NewGuid().ToString(), Large = large }; + + // Use slow stream to create a window for cancellation. + byte[] payload = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(doc)); + using SlowCancelableStream slow = new(payload, chunkSize: 64, perReadDelayMs: 1); + using CancellationTokenSource cts = new(); + EncryptionOptions options = CreateEncryptionOptions(new[] { "/Large" }); + Task encryptTask = EncryptionProcessor.EncryptAsync(slow, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), cts.Token); + cts.CancelAfter(5); // cancel shortly after start + + try + { + await encryptTask; + Assert.Fail("Expected cancellation"); + } + catch (Exception ex) + { + Assert.IsTrue(ex is OperationCanceledException, $"Expected OperationCanceledException but got {ex.GetType()}"); + } + } + + [TestMethod] + public async Task Decrypt_Cancellation_Aborts() + { + // First create a valid encrypted payload + string large = new string('y', 50_000); + var doc = new { id = Guid.NewGuid().ToString(), Large = large }; + EncryptionOptions options = CreateEncryptionOptions(new[] { "/Large" }); + Stream encrypted = await EncryptionProcessor.EncryptAsync(TestCommon.ToStream(doc), mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + + // Wrap encrypted stream in slow stream (must be seekable; we copy bytes) + byte[] bytes = ((MemoryStream)encrypted).ToArray(); + SlowCancelableStream slow = new(bytes, chunkSize: 64, perReadDelayMs: 1); + using CancellationTokenSource cts = new(); + Task<(Stream, DecryptionContext)> decryptTask = EncryptionProcessor.DecryptAsync( + slow, + mockEncryptor.Object, + new CosmosDiagnosticsContext(), + RequestOptionsOverrideHelper.Create(JsonProcessor.Stream), + cts.Token); + + cts.CancelAfter(5); + try + { + await decryptTask; + Assert.Fail("Expected cancellation"); + } + catch (Exception ex) + { + Assert.IsTrue(ex is OperationCanceledException, $"Expected OperationCanceledException but got {ex.GetType()}"); + } + } + + private sealed class SlowCancelableStream : Stream + { + private readonly byte[] data; + private readonly int chunkSize; + private readonly int perReadDelayMs; + private long position; + + public SlowCancelableStream(byte[] data, int chunkSize, int perReadDelayMs) + { + this.data = data; + this.chunkSize = chunkSize; + this.perReadDelayMs = perReadDelayMs; + this.position = 0; + } + + public override bool CanRead => true; + public override bool CanSeek => true; + public override bool CanWrite => false; + public override long Length => this.data.Length; + public override long Position { get => this.position; set => this.position = value; } + public override void Flush() { } + public override long Seek(long offset, SeekOrigin origin) + { + long newPos = origin switch + { + SeekOrigin.Begin => offset, + SeekOrigin.Current => this.position + offset, + SeekOrigin.End => this.data.Length + offset, + _ => this.position, + }; + if (newPos < 0 || newPos > this.data.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + this.position = newPos; + return this.position; + } + public override void SetLength(long value) => throw new NotSupportedException(); + public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException("Synchronous Read not used"); + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default) + { + return new ValueTask(this.ReadInternalAsync(destination, cancellationToken)); + } + + private async Task ReadInternalAsync(Memory destination, CancellationToken cancellationToken) + { + if (this.position >= this.data.Length) + { + return 0; + } + + if (this.perReadDelayMs > 0) + { + await Task.Delay(this.perReadDelayMs, cancellationToken); + } + + int remaining = (int)Math.Min(this.data.Length - this.position, this.chunkSize); + int toCopy = Math.Min(remaining, destination.Length); + this.data.AsSpan((int)this.position, toCopy).CopyTo(destination.Span); + this.position += toCopy; + return toCopy; + } + } + } +} +#endif \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestCommon.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestCommon.cs index 4e36dc899a..8701960c2b 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestCommon.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestCommon.cs @@ -87,14 +87,27 @@ public TestDoc() public override bool Equals(object obj) { - return obj is TestDoc doc - && this.Id == doc.Id + if (!(obj is TestDoc doc)) + { + return false; + } + + bool arraysEqual = (this.SensitiveArr == null && doc.SensitiveArr == null) || + (this.SensitiveArr != null && doc.SensitiveArr != null && + this.SensitiveArr.SequenceEqual(doc.SensitiveArr)); + + bool dictsEqual = (this.SensitiveDict == null && doc.SensitiveDict == null) || + (this.SensitiveDict != null && doc.SensitiveDict != null && + this.SensitiveDict.Count == doc.SensitiveDict.Count && + this.SensitiveDict.All(kv => doc.SensitiveDict.ContainsKey(kv.Key) && doc.SensitiveDict[kv.Key] == kv.Value)); + + return this.Id == doc.Id && this.PK == doc.PK && this.NonSensitive == doc.NonSensitive && this.SensitiveInt == doc.SensitiveInt && this.SensitiveStr == doc.SensitiveStr - && this.SensitiveArr?.Equals(doc.SensitiveArr) == true - && this.SensitiveDict?.Equals(doc.SensitiveDict) == true; + && arraysEqual + && dictsEqual; } public override int GetHashCode() diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestEncryptionKeyStoreProvider.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestEncryptionKeyStoreProvider.cs new file mode 100644 index 0000000000..46682bbd80 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestEncryptionKeyStoreProvider.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System.Linq; + using Microsoft.Data.Encryption.Cryptography; + + internal sealed class TestEncryptionKeyStoreProvider : EncryptionKeyStoreProvider + { + public int UnwrapCalls { get; private set; } + public int WrapCalls { get; private set; } + + public byte[] DerivedRawKey { get; } = Enumerable.Range(0, 32).Select(static i => (byte)(255 - i)).ToArray(); + + public override string ProviderName => "test-store"; + + public override byte[] UnwrapKey(string encryptionKeyId, KeyEncryptionKeyAlgorithm algorithm, byte[] encryptedKey) + { + this.UnwrapCalls++; + + return this.DerivedRawKey; + } + + public override byte[] WrapKey(string encryptionKeyId, KeyEncryptionKeyAlgorithm algorithm, byte[] key) + { + this.WrapCalls++; + + return key; + } + + public override byte[] Sign(string encryptionKeyId, bool allowEnclaveComputations) + { + return new byte[] { 0x01 }; + } + + public override bool Verify(string encryptionKeyId, bool allowEnclaveComputations, byte[] signature) + { + return signature?.Length == 1 && signature[0] == 0x01; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestEncryptorFactory.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestEncryptorFactory.cs new file mode 100644 index 0000000000..e3b6c1968e --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestEncryptorFactory.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Encryption.Custom; + using Moq; + + /// + /// Shared helper for creating mock Encryptor (and DataEncryptionKey for MDE) instances used in tests. + /// Reduces repetitive Moq setup code across test classes. + /// + internal static class TestEncryptorFactory + { + public static Mock CreateMde(string dekId, out Mock dekMock) + { + Mock localDek = new Mock(); + localDek.SetupGet(d => d.EncryptionAlgorithm).Returns(CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized); + localDek.Setup(d => d.GetEncryptByteCount(It.IsAny())).Returns(i => i); + localDek.Setup(d => d.GetDecryptByteCount(It.IsAny())).Returns(i => i); + localDek.Setup(d => d.EncryptData(It.IsAny())).Returns(b => TestCommon.EncryptData(b)); + localDek.Setup(d => d.EncryptData(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((byte[] input, int offset, int length, byte[] output, int outputOffset) => TestCommon.EncryptData(input, offset, length, output, outputOffset)); + localDek.Setup(d => d.DecryptData(It.IsAny())).Returns(b => TestCommon.DecryptData(b)); + localDek.Setup(d => d.DecryptData(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((byte[] input, int offset, int length, byte[] output, int outputOffset) => TestCommon.DecryptData(input, offset, length, output, outputOffset)); + + Mock encryptor = new Mock(); + encryptor.Setup(e => e.GetEncryptionKeyAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((string id, string algo, CancellationToken t) => id == dekId ? localDek.Object : throw new InvalidOperationException("DEK not found")); + encryptor.Setup(e => e.EncryptAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((byte[] plain, string id, string algo, CancellationToken t) => id == dekId ? TestCommon.EncryptData(plain) : throw new InvalidOperationException("DEK not found")); + encryptor.Setup(e => e.DecryptAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((byte[] cipher, string id, string algo, CancellationToken t) => id == dekId ? TestCommon.DecryptData(cipher) : throw new InvalidOperationException("DEK not found")); + + dekMock = localDek; + return encryptor; + } + + public static Mock CreateLegacy(string dekId) + { + Mock encryptor = new Mock(); + encryptor.Setup(e => e.EncryptAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((byte[] plain, string id, string algo, CancellationToken t) => id == dekId ? TestCommon.EncryptData(plain) : throw new InvalidOperationException("DEK not found")); + encryptor.Setup(e => e.DecryptAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((byte[] cipher, string id, string algo, CancellationToken t) => id == dekId ? TestCommon.DecryptData(cipher) : throw new InvalidOperationException("Null DEK was returned.")); + return encryptor; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestableCosmosDiagnosticsContext.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestableCosmosDiagnosticsContext.cs new file mode 100644 index 0000000000..dac8dddf5d --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/TestableCosmosDiagnosticsContext.cs @@ -0,0 +1,125 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Tests +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + + /// + /// Testable wrapper for CosmosDiagnosticsContext that records scope information for test verification. + /// + internal class TestableCosmosDiagnosticsContext + { + private readonly CosmosDiagnosticsContext inner; + private readonly List records = new List(4); + private readonly object recordsLock = new object(); + + public TestableCosmosDiagnosticsContext() + { + this.inner = CosmosDiagnosticsContext.Create(null); + } + + /// + /// Gets the inner CosmosDiagnosticsContext for passing to APIs. + /// + public CosmosDiagnosticsContext Inner => this.inner; + + /// + /// Recorded scope metadata. + /// + public readonly struct ScopeRecord + { + public ScopeRecord(string name, long startTimestamp, long elapsedTicks) + { + this.Name = name; + this.StartTimestamp = startTimestamp; + this.ElapsedTicks = elapsedTicks; + } + + public string Name { get; } + + public long StartTimestamp { get; } + + public long ElapsedTicks { get; } + + public TimeSpan Elapsed => TimeSpan.FromTicks(this.ElapsedTicks); + } + + /// + /// Gets recorded scope names for test verification. + /// + public IReadOnlyList Scopes + { + get + { + lock (this.recordsLock) + { + if (this.records.Count == 0) + { + return Array.Empty(); + } + + string[] names = new string[this.records.Count]; + for (int i = 0; i < this.records.Count; i++) + { + names[i] = this.records[i].Name; + } + + return names; + } + } + } + + /// + /// Creates a testable scope that records timing information. + /// + public TestableScope CreateScope(string scope) + { + CosmosDiagnosticsContext.Scope innerScope = this.inner.CreateScope(scope); + return new TestableScope(this, scope, innerScope); + } + + private void Record(string name, long startTicks, long elapsedTicks) + { + lock (this.recordsLock) + { + this.records.Add(new ScopeRecord(name, startTicks, elapsedTicks)); + } + } + + /// + /// Testable scope wrapper that records timing information for tests. + /// + public sealed class TestableScope : IDisposable + { + private readonly TestableCosmosDiagnosticsContext owner; + private readonly string name; + private readonly long startTicks; + private readonly CosmosDiagnosticsContext.Scope innerScope; + private bool isDisposed; + + internal TestableScope(TestableCosmosDiagnosticsContext owner, string name, CosmosDiagnosticsContext.Scope innerScope) + { + this.owner = owner; + this.name = name; + this.startTicks = Stopwatch.GetTimestamp(); + this.innerScope = innerScope; + } + + public void Dispose() + { + // Only record the first dispose call (idempotent) + if (!this.isDisposed) + { + this.isDisposed = true; + long elapsedTicks = Stopwatch.GetTimestamp() - this.startTicks; + this.owner.Record(this.name, this.startTicks, elapsedTicks); + this.innerScope.Dispose(); + } + } + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/Adapters/NewtonsoftAdapterTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/Adapters/NewtonsoftAdapterTests.cs new file mode 100644 index 0000000000..94a3fedca7 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/Adapters/NewtonsoftAdapterTests.cs @@ -0,0 +1,161 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos.Encryption.Tests.Transformation.Adapters +{ + using System; + using System.IO; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Encryption.Custom; + using Microsoft.Azure.Cosmos.Encryption.Custom.Transformation; + using Microsoft.Azure.Cosmos.Encryption.Tests; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Newtonsoft.Json; + using Newtonsoft.Json.Linq; + + [TestClass] + public class NewtonsoftAdapterTests + { + private const string DekId = "dek-id"; + private static Mock mockEncryptor = null!; + private static EncryptionOptions defaultOptions = null!; + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + _ = context; + mockEncryptor = TestEncryptorFactory.CreateMde(DekId, out _); + defaultOptions = new EncryptionOptions + { + DataEncryptionKeyId = DekId, +#pragma warning disable CS0618 + EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, +#pragma warning restore CS0618 + PathsToEncrypt = new[] { "/Sensitive" }, + }; + } + + [TestMethod] + public async Task EncryptAsync_AppendsEncryptionMetadata() + { + NewtonsoftAdapter adapter = new (new MdeJObjectEncryptionProcessor()); + using Stream input = TestCommon.ToStream(new { id = "1", Sensitive = "secret" }); + + Stream encrypted = await adapter.EncryptAsync(input, mockEncryptor.Object, defaultOptions, CancellationToken.None); + JObject result = Read(encrypted); + + Assert.IsTrue(result.TryGetValue(Constants.EncryptedInfo, out JToken ei), "Expected encrypted metadata"); + Assert.AreEqual(JTokenType.String, result["Sensitive"].Type); + Assert.IsNotNull(ei); + } + + [TestMethod] + public async Task EncryptAsync_StreamOverload_Throws() + { + NewtonsoftAdapter adapter = new (new MdeJObjectEncryptionProcessor()); + using Stream input = TestCommon.ToStream(new { id = "1", Sensitive = "secret" }); + using MemoryStream output = new (); + + await Assert.ThrowsExceptionAsync( + () => adapter.EncryptAsync(input, output, mockEncryptor.Object, defaultOptions, JsonProcessor.Newtonsoft, CancellationToken.None)); + } + + [TestMethod] + public async Task DecryptAsync_WhenNoMetadata_ReturnsOriginalStream() + { + NewtonsoftAdapter adapter = new (new MdeJObjectEncryptionProcessor()); + using MemoryStream input = new (Encoding.UTF8.GetBytes("{\"id\":\"1\"}")); + CosmosDiagnosticsContext diagnostics = new CosmosDiagnosticsContext(); + + (Stream result, DecryptionContext context) = await adapter.DecryptAsync(input, mockEncryptor.Object, diagnostics, CancellationToken.None); + + Assert.AreSame(input, result); + Assert.IsNull(context); + Assert.AreEqual(0, result.Position); + } + + [TestMethod] + public async Task DecryptAsync_WhenLegacyAlgorithm_ReturnsOriginalStream() + { + NewtonsoftAdapter adapter = new (new MdeJObjectEncryptionProcessor()); + + #pragma warning disable CS0618 + EncryptionProperties legacyProps = new ( + encryptionFormatVersion: 2, + encryptionAlgorithm: CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized, + dataEncryptionKeyId: "legacy-dek", + encryptedData: null, + encryptedPaths: new[] { "/Sensitive" }); + #pragma warning restore CS0618 + JObject legacyDoc = new () + { + ["id"] = "1", + [Constants.EncryptedInfo] = JObject.FromObject(legacyProps), + }; + + using MemoryStream input = new (Encoding.UTF8.GetBytes(legacyDoc.ToString(Formatting.None))); + CosmosDiagnosticsContext diagnostics = new CosmosDiagnosticsContext(); + + (Stream result, DecryptionContext context) = await adapter.DecryptAsync(input, mockEncryptor.Object, diagnostics, CancellationToken.None); + + Assert.AreSame(input, result); + Assert.IsNull(context); + Assert.AreEqual(0, result.Position); + } + + [TestMethod] + public async Task DecryptAsync_StreamOverload_WritesDecryptedPayload() + { + NewtonsoftAdapter adapter = new (new MdeJObjectEncryptionProcessor()); + Stream encrypted = await CreateEncryptedPayloadAsync(adapter); + + using MemoryStream output = new (); + CosmosDiagnosticsContext diagnostics = new CosmosDiagnosticsContext(); + DecryptionContext context = await adapter.DecryptAsync(encrypted, output, mockEncryptor.Object, diagnostics, CancellationToken.None); + + Assert.IsNotNull(context); + + JObject roundTripped = Read(output); + Assert.IsFalse(roundTripped.ContainsKey(Constants.EncryptedInfo)); + Assert.AreEqual("secret", roundTripped["Sensitive"].ToString()); + } + + [TestMethod] + public async Task DecryptAsync_ReturnsDecryptedStream() + { + NewtonsoftAdapter adapter = new (new MdeJObjectEncryptionProcessor()); + Stream encrypted = await CreateEncryptedPayloadAsync(adapter); + CosmosDiagnosticsContext diagnostics = new CosmosDiagnosticsContext(); + + (Stream decryptedStream, DecryptionContext context) = await adapter.DecryptAsync(encrypted, mockEncryptor.Object, diagnostics, CancellationToken.None); + + Assert.IsNotNull(context); + Assert.AreNotSame(encrypted, decryptedStream); + + JObject roundTripped = Read(decryptedStream); + Assert.IsFalse(roundTripped.ContainsKey(Constants.EncryptedInfo)); + Assert.AreEqual("secret", roundTripped["Sensitive"].ToString()); + Assert.IsTrue(context.DecryptionInfoList[0].PathsDecrypted.Any(p => p == "/Sensitive")); + } + + private static async Task CreateEncryptedPayloadAsync(NewtonsoftAdapter adapter) + { + using Stream input = TestCommon.ToStream(new { id = "1", Sensitive = "secret" }); + Stream encrypted = await adapter.EncryptAsync(input, mockEncryptor.Object, defaultOptions, CancellationToken.None); + encrypted.Position = 0; + return encrypted; + } + + private static JObject Read(Stream stream) + { + stream.Position = 0; + using StreamReader reader = new (stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true); + using JsonTextReader jsonReader = new (reader); + return JObject.Load(jsonReader); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/Adapters/SystemTextJsonStreamAdapterTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/Adapters/SystemTextJsonStreamAdapterTests.cs new file mode 100644 index 0000000000..2173fb24db --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/Adapters/SystemTextJsonStreamAdapterTests.cs @@ -0,0 +1,191 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +#if NET8_0_OR_GREATER +namespace Microsoft.Azure.Cosmos.Encryption.Tests.Transformation.Adapters +{ + using System; + using System.IO; + using System.Text; + using System.Text.Json; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Encryption.Custom; + using Microsoft.Azure.Cosmos.Encryption.Custom.Transformation; + using Microsoft.Azure.Cosmos.Encryption.Tests; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + + [TestClass] + public class SystemTextJsonSystemTextJsonStreamAdapterTests + { + private const string DekId = "dek-id"; + private static Mock mockEncryptor = null!; + private static EncryptionOptions defaultOptions = null!; + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + _ = context; + mockEncryptor = TestEncryptorFactory.CreateMde(DekId, out _); + defaultOptions = new EncryptionOptions + { + DataEncryptionKeyId = DekId, +#pragma warning disable CS0618 + EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, +#pragma warning restore CS0618 + PathsToEncrypt = new[] { "/Sensitive" }, + }; + } + + [TestMethod] + public async Task EncryptAsync_ReturnsEncryptedStream() + { + SystemTextJsonStreamAdapter adapter = new (new StreamProcessor()); + using Stream input = TestCommon.ToStream(new { id = "1", Sensitive = "secret" }); + + Stream encrypted = await adapter.EncryptAsync(input, mockEncryptor.Object, defaultOptions, CancellationToken.None); + using JsonDocument doc = JsonDocument.Parse(encrypted, new JsonDocumentOptions { AllowTrailingCommas = true }); + + Assert.IsTrue(doc.RootElement.TryGetProperty(Constants.EncryptedInfo, out JsonElement ei)); + Assert.AreEqual(JsonValueKind.Object, ei.ValueKind); + } + + [TestMethod] + public async Task EncryptAsync_StreamOverload_WritesToOutput() + { + SystemTextJsonStreamAdapter adapter = new (new StreamProcessor()); + using Stream input = TestCommon.ToStream(new { id = "1", Sensitive = "secret" }); + using MemoryStream output = new (); + + await adapter.EncryptAsync(input, output, mockEncryptor.Object, defaultOptions, JsonProcessor.Stream, CancellationToken.None); + + output.Position = 0; + using JsonDocument doc = JsonDocument.Parse(output); + Assert.IsTrue(doc.RootElement.TryGetProperty(Constants.EncryptedInfo, out _)); + } + + [TestMethod] + public async Task EncryptAsync_StreamOverload_WithNonStreamProcessor_Throws() + { + SystemTextJsonStreamAdapter adapter = new (new StreamProcessor()); + using Stream input = TestCommon.ToStream(new { id = "1" }); + using MemoryStream output = new (); + + EncryptionOptions wrongOptions = new EncryptionOptions + { + DataEncryptionKeyId = DekId, +#pragma warning disable CS0618 + EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, +#pragma warning restore CS0618 + PathsToEncrypt = new[] { "/Sensitive" }, + }; + + await Assert.ThrowsExceptionAsync( + () => adapter.EncryptAsync(input, output, mockEncryptor.Object, wrongOptions, JsonProcessor.Newtonsoft, CancellationToken.None)); + } + + [TestMethod] + public async Task DecryptAsync_WhenNoMetadata_ReturnsOriginalStream() + { + SystemTextJsonStreamAdapter adapter = new (new StreamProcessor()); + using MemoryStream input = new (Encoding.UTF8.GetBytes("{\"id\":\"1\"}")); + CosmosDiagnosticsContext diagnostics = new CosmosDiagnosticsContext(); + + (Stream result, DecryptionContext context) = await adapter.DecryptAsync(input, mockEncryptor.Object, diagnostics, CancellationToken.None); + + Assert.AreSame(input, result); + Assert.IsNull(context); + Assert.AreEqual(0, result.Position); + } + + [TestMethod] + public async Task DecryptAsync_ReturnsDecryptedStream() + { + SystemTextJsonStreamAdapter adapter = new (new StreamProcessor()); + Stream encrypted = await CreateEncryptedPayloadAsync(adapter); + CosmosDiagnosticsContext diagnostics = new CosmosDiagnosticsContext(); + + (Stream decrypted, DecryptionContext context) = await adapter.DecryptAsync(encrypted, mockEncryptor.Object, diagnostics, CancellationToken.None); + + Assert.IsNotNull(context); + Assert.AreNotSame(encrypted, decrypted); + + using JsonDocument doc = JsonDocument.Parse(decrypted); + Assert.AreEqual("secret", doc.RootElement.GetProperty("Sensitive").GetString()); + } + + [TestMethod] + public async Task DecryptAsync_OutputStream_WritesDecryptedPayload() + { + SystemTextJsonStreamAdapter adapter = new (new StreamProcessor()); + Stream encrypted = await CreateEncryptedPayloadAsync(adapter); + CosmosDiagnosticsContext diagnostics = new CosmosDiagnosticsContext(); + using MemoryStream output = new (); + + DecryptionContext context = await adapter.DecryptAsync(encrypted, output, mockEncryptor.Object, diagnostics, CancellationToken.None); + + Assert.IsNotNull(context); + output.Position = 0; + using JsonDocument doc = JsonDocument.Parse(output); + Assert.AreEqual("secret", doc.RootElement.GetProperty("Sensitive").GetString()); + } + + [TestMethod] + public async Task DecryptAsync_OutputStream_WithNonEncryptedPayload_ReturnsNull() + { + SystemTextJsonStreamAdapter adapter = new (new StreamProcessor()); + using MemoryStream input = new (Encoding.UTF8.GetBytes("{\"id\":1}")); + using MemoryStream output = new (); + CosmosDiagnosticsContext diagnostics = new CosmosDiagnosticsContext(); + + DecryptionContext context = await adapter.DecryptAsync(input, output, mockEncryptor.Object, diagnostics, CancellationToken.None); + + Assert.IsNull(context); + Assert.AreEqual(0, input.Position); + Assert.AreEqual(0, output.Length); + } + + [TestMethod] + public async Task DecryptAsync_WithLegacyAlgorithm_Throws() + { + SystemTextJsonStreamAdapter adapter = new (new StreamProcessor()); + EncryptionProperties legacyProps = CreateLegacyEncryptionProperties(); + EncryptionPropertiesWrapper wrapper = new (legacyProps); + byte[] payload = JsonSerializer.SerializeToUtf8Bytes(wrapper); + using MemoryStream input = new (payload); + CosmosDiagnosticsContext diagnostics = new CosmosDiagnosticsContext(); + + NotSupportedException exception = await Assert.ThrowsExceptionAsync(async () => + { + await adapter.DecryptAsync(input, mockEncryptor.Object, diagnostics, CancellationToken.None); + }); + + Assert.IsTrue(exception.Message.Contains("not supported"), $"Unexpected exception message: {exception.Message}"); +#pragma warning disable CS0618 + Assert.IsTrue(exception.Message.Contains(CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized), $"Exception should mention the unsupported algorithm"); +#pragma warning restore CS0618 + } + + private static async Task CreateEncryptedPayloadAsync(SystemTextJsonStreamAdapter adapter) + { + using Stream input = TestCommon.ToStream(new { id = "1", Sensitive = "secret" }); + Stream encrypted = await adapter.EncryptAsync(input, mockEncryptor.Object, defaultOptions, CancellationToken.None); + encrypted.Position = 0; + return encrypted; + } + + private static EncryptionProperties CreateLegacyEncryptionProperties() + { +#pragma warning disable CS0618 + return new EncryptionProperties( + encryptionFormatVersion: 2, + encryptionAlgorithm: CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized, + dataEncryptionKeyId: "legacy-dek", + encryptedData: null, + encryptedPaths: new[] { "/Sensitive" }); +#pragma warning restore CS0618 + } + } +} +#endif diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/StreamProcessorDecryptorTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/StreamProcessorDecryptorTests.cs new file mode 100644 index 0000000000..0079175aa5 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/StreamProcessorDecryptorTests.cs @@ -0,0 +1,645 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +#if NET8_0_OR_GREATER +namespace Microsoft.Azure.Cosmos.Encryption.Tests.Transformation +{ + using System; + using System.Buffers.Text; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using System.Text.Json; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Encryption.Custom; + using Microsoft.Azure.Cosmos.Encryption.Custom.Transformation; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + + /// + /// Focused tests for StreamProcessor.DecryptStreamAsync logic (string/number/bool/null/object/array branches, + /// buffer growth/leftOver logic, compression handling, skipping of _ei, invalid versions and algorithms etc). + /// We intentionally mock only Encryptor + DataEncryptionKey and use real MdeEncryptor to avoid reflection. + /// + [TestClass] + public class StreamProcessorDecryptorTests + { + private const string DekId = "dekId"; + private static Mock mockEncryptor; + private static Mock mockDek; + + private static readonly JsonSerializerOptions SystemTextOptions = new() + { + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip, + }; + + [ClassInitialize] + public static void Init(TestContext ctx) + { + _ = ctx; + StreamProcessor.InitialBufferSize = 8; // force multiple resizes / leftover path + + mockEncryptor = TestEncryptorFactory.CreateMde(DekId, out mockDek); + } + + private static EncryptionOptions CreateOptions(IEnumerable paths) + { + return new EncryptionOptions + { + DataEncryptionKeyId = DekId, + EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + PathsToEncrypt = paths.ToList() + }; + } + + private static async Task<(MemoryStream encrypted, EncryptionProperties properties)> EncryptRawAsync(object doc, EncryptionOptions options) + { + Stream input = TestCommon.ToStream(doc); + MemoryStream encryptedStream = new(); + await EncryptionProcessor.EncryptAsync(input, encryptedStream, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + encryptedStream.Position = 0; + + // get properties via System.Text.Json to assert later + using JsonDocument jd = JsonDocument.Parse(encryptedStream, new JsonDocumentOptions { AllowTrailingCommas = true }); + JsonElement root = jd.RootElement; + JsonElement ei = root.GetProperty(Constants.EncryptedInfo); + EncryptionProperties props = JsonSerializer.Deserialize(ei.GetRawText(), SystemTextOptions); + encryptedStream.Position = 0; + return ((MemoryStream)encryptedStream, props); + } + + [TestMethod] + public async Task Decrypt_AllPrimitiveTypesAndContainers() + { + // Arrange + var doc = new + { + id = Guid.NewGuid().ToString(), + SensitiveStr = "abc", + SensitiveInt = 123, + SensitiveBoolTrue = true, + SensitiveBoolFalse = false, + SensitiveNull = (string)null, + SensitiveArr = new object[] { 1, 2, 3 }, + SensitiveObj = new { a = 5, b = "text" }, + NonSensitive = 999 + }; + string[] paths = new[] { "/SensitiveStr", "/SensitiveInt", "/SensitiveBoolTrue", "/SensitiveBoolFalse", "/SensitiveNull", "/SensitiveArr", "/SensitiveObj" }; + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + + // Act + MemoryStream output = new(); + DecryptionContext ctx = await new StreamProcessor().DecryptStreamAsync(encrypted, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + + // Assert + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output); + JsonElement root = jd.RootElement; + foreach (string p in paths) + { + string name = p.TrimStart('/'); + Assert.IsTrue(root.TryGetProperty(name, out JsonElement _)); + // Null values are not encrypted -> not present in decrypted paths list. + if (p == "/SensitiveNull") + { + Assert.IsFalse(ctx.DecryptionInfoList[0].PathsDecrypted.Contains(p)); + } + else + { + Assert.IsTrue(ctx.DecryptionInfoList[0].PathsDecrypted.Contains(p)); + } + } + } + + [TestMethod] + public async Task Decrypt_Skips_EncryptionInfo_Block() + { + // Arrange + // Build a document that already has _ei. (Encryptor will append another one during encryption; we want to ensure decryptor skips only the encrypted one at top-level.) + var doc = new { id = "1", _ei = new { ignore = true }, SensitiveStr = "abc" }; + string[] paths = new[] { "/SensitiveStr" }; + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + + // Act + MemoryStream output = new(); + DecryptionContext ctx = await new StreamProcessor().DecryptStreamAsync(encrypted, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + + // Assert + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output); + JsonElement root = jd.RootElement; + Assert.IsFalse(root.TryGetProperty(Constants.EncryptedInfo, out _)); + Assert.AreEqual("abc", root.GetProperty("SensitiveStr").GetString()); + Assert.AreEqual(1, ctx.DecryptionInfoList.Count); + } + + [TestMethod] + public async Task Decrypt_IgnoresUnknownPropertyTypesAndMaintainsJson() + { + // Arrange + var doc = new { id = "1", SensitiveStr = "abc", Regular = 5 }; + string[] paths = new[] { "/SensitiveStr" }; + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + + // Act + MemoryStream output = new(); + _ = await new StreamProcessor().DecryptStreamAsync(encrypted, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output); + // Assert + JsonElement root = jd.RootElement; + Assert.AreEqual(5, root.GetProperty("Regular").GetInt32()); + } + + [TestMethod] + public async Task Decrypt_Throws_OnUnknownEncryptionFormatVersion() + { + // Arrange + var doc = new { id = "1", SensitiveStr = "abc" }; + string[] paths = new[] { "/SensitiveStr" }; + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + EncryptionProperties invalid = new EncryptionProperties(999, props.EncryptionAlgorithm, props.DataEncryptionKeyId, null, props.EncryptedPaths); + // Act + Assert + MemoryStream output = new(); + await Assert.ThrowsExceptionAsync(() => new StreamProcessor().DecryptStreamAsync(encrypted, output, mockEncryptor.Object, invalid, new CosmosDiagnosticsContext(), CancellationToken.None)); + } + + [TestMethod] + public async Task Decrypt_Throws_OnInvalidBase64Ciphertext() + { + // Arrange + var doc = new { id = "1", SensitiveStr = "abc" }; + string[] paths = new[] { "/SensitiveStr" }; + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + string jsonText = Encoding.UTF8.GetString(encrypted.ToArray()); + using JsonDocument jd = JsonDocument.Parse(jsonText); + string originalCipher = jd.RootElement.GetProperty("SensitiveStr").GetString(); + Assert.IsNotNull(originalCipher); + string corruptedCipher = string.Concat("#", originalCipher.AsSpan(1)); // invalid base64 start + jsonText = jsonText.Replace("\"SensitiveStr\":\"" + originalCipher + "\"", "\"SensitiveStr\":\"" + corruptedCipher + "\""); + MemoryStream corruptedStream = new(Encoding.UTF8.GetBytes(jsonText)); + // Act + Assert + MemoryStream output = new(); + await Assert.ThrowsExceptionAsync(() => new StreamProcessor().DecryptStreamAsync(corruptedStream, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None)); + } + + [TestMethod] + public async Task Decrypt_IgnoredBlock_PartialEiSkip() + { + // Arrange + // Force the _ei metadata object to span multiple buffer reads so Utf8JsonReader.TrySkip() returns false, + // exercising the fallback isIgnoredBlock path. + const int propertyCount = 250; // large to inflate _ei encrypted paths list + Dictionary doc = new() { ["id"] = "1" }; + List paths = new(propertyCount); + for (int i = 0; i < propertyCount; i++) + { + string name = "P" + i.ToString(); + // moderately sized value to enlarge encrypted base64 + metadata + string value = new string('x', 32 + (i % 5)); + doc[name] = value; + paths.Add("/" + name); + } + + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + + // Choose very small initial buffer so _ei object is fragmented. + int original = StreamProcessor.InitialBufferSize; + StreamProcessor.InitialBufferSize = 32; + try + { + // Act + MemoryStream output = new(); + DecryptionContext ctx = await new StreamProcessor().DecryptStreamAsync(encrypted, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output, new JsonDocumentOptions { AllowTrailingCommas = true }); + JsonElement root = jd.RootElement; + // Assert + // _ei must be removed + Assert.IsFalse(root.TryGetProperty(Constants.EncryptedInfo, out _), "_ei should be skipped"); + // spot check a few decrypted properties + Assert.AreEqual(JsonValueKind.String, root.GetProperty("P0").ValueKind); + Assert.AreEqual(JsonValueKind.String, root.GetProperty("P100").ValueKind); + // Ensure some decrypted paths recorded + Assert.IsTrue(ctx.DecryptionInfoList[0].PathsDecrypted.Count > 200); + } + finally + { + StreamProcessor.InitialBufferSize = original; // restore + } + } + + [TestMethod] + public async Task Decrypt_UnencryptedArrayAndBooleans() + { + // Arrange + // Covers StartArray / EndArray / True / False switch branches where decryptPropertyName == null (no encryption for those tokens). + var doc = new + { + id = "1", + SensitiveStr = "secret", + UnencryptedArr = new int[] { 7, 8, 9 }, + UnencryptedBoolTrue = true, + UnencryptedBoolFalse = false, + }; + string[] paths = new[] { "/SensitiveStr" }; // only encrypt the string; others remain plain + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + + // Act + MemoryStream output = new(); + DecryptionContext ctx = await new StreamProcessor().DecryptStreamAsync(encrypted, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + + // Assert + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output); + JsonElement root = jd.RootElement; + // Ensure decrypted sensitive property was processed + Assert.AreEqual("secret", root.GetProperty("SensitiveStr").GetString()); + Assert.IsTrue(ctx.DecryptionInfoList[0].PathsDecrypted.Contains("/SensitiveStr")); + // Validate unencrypted array preserved + JsonElement arr = root.GetProperty("UnencryptedArr"); + Assert.AreEqual(JsonValueKind.Array, arr.ValueKind); + Assert.AreEqual(3, arr.GetArrayLength()); + Assert.AreEqual(7, arr[0].GetInt32()); + Assert.AreEqual(8, arr[1].GetInt32()); + Assert.AreEqual(9, arr[2].GetInt32()); + // Validate unencrypted booleans preserved + Assert.IsTrue(root.GetProperty("UnencryptedBoolTrue").GetBoolean()); + Assert.IsFalse(root.GetProperty("UnencryptedBoolFalse").GetBoolean()); + } + + [TestMethod] + public async Task Decrypt_ForgedCipherText_TypeMarkerNull() + { + // Arrange + // Covers TypeMarker.Null switch branch by forging a ciphertext with first byte = Null marker. + var doc = new { id = "1", SensitiveStr = "abc" }; + string[] paths = new[] { "/SensitiveStr" }; + EncryptionOptions options = CreateOptions(paths); // no compression + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + Assert.AreEqual(EncryptionFormatVersion.Mde, props.EncryptionFormatVersion); + + // Parse and replace SensitiveStr base64 value + encrypted.Position = 0; + using JsonDocument jd = JsonDocument.Parse(encrypted, new JsonDocumentOptions { AllowTrailingCommas = true }); + string originalCipher = jd.RootElement.GetProperty("SensitiveStr").GetString(); + Assert.IsNotNull(originalCipher); + byte[] forgedBytes = new byte[] { (byte)TypeMarker.Null, 0x00 }; // minimal payload + string forgedBase64 = Convert.ToBase64String(forgedBytes); + + // Reconstruct JSON deterministically + MemoryStream forged = new(); + using (Utf8JsonWriter w = new(forged)) + { + w.WriteStartObject(); + w.WriteString("id", "1"); + w.WriteString("SensitiveStr", forgedBase64); + w.WritePropertyName(Constants.EncryptedInfo); + jd.RootElement.GetProperty(Constants.EncryptedInfo).WriteTo(w); + w.WriteEndObject(); + } + forged.Position = 0; + + // Act + // Use custom encryptor that returns empty plaintext for Null marker + StreamProcessor sp = new StreamProcessor { Encryptor = new NullMarkerMdeEncryptor() }; + MemoryStream output = new(); + DecryptionContext ctx = await sp.DecryptStreamAsync(forged, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + // Assert + output.Position = 0; + using JsonDocument outDoc = JsonDocument.Parse(output); + Assert.AreEqual(JsonValueKind.Null, outDoc.RootElement.GetProperty("SensitiveStr").ValueKind); + Assert.IsTrue(ctx.DecryptionInfoList[0].PathsDecrypted.Contains("/SensitiveStr")); + } + + [TestMethod] + public async Task Decrypt_Throws_OnMissingDataEncryptionKey() + { + // Arrange + // Arrange: create a valid encrypted payload + var doc = new { id = "1", SensitiveStr = "abc" }; + string[] paths = new[] { "/SensitiveStr" }; + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + + // Forge properties with an unknown DEK id + EncryptionProperties badProps = new( + props.EncryptionFormatVersion, + props.EncryptionAlgorithm, + dataEncryptionKeyId: "missing-dek", + encryptedData: null, + props.EncryptedPaths); + + // Act + Assert + MemoryStream output = new(); + await Assert.ThrowsExceptionAsync(() => new StreamProcessor().DecryptStreamAsync(encrypted, output, mockEncryptor.Object, badProps, new CosmosDiagnosticsContext(), CancellationToken.None)); + } + + [TestMethod] + public async Task Decrypt_EncryptedPathValueIsNumber_NoDecryptionOccurs() + { + // Arrange + var doc = new { id = "1", SensitiveStr = "abc" }; + string[] paths = new[] { "/SensitiveStr" }; + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + + // Replace the encrypted string token with a number token to bypass decryption logic for that property + string jsonText = Encoding.UTF8.GetString(encrypted.ToArray()); + using (JsonDocument jd = JsonDocument.Parse(jsonText)) + { + string originalCipher = jd.RootElement.GetProperty("SensitiveStr").GetString(); + Assert.IsNotNull(originalCipher); + jsonText = jsonText.Replace("\"SensitiveStr\":\"" + originalCipher + "\"", "\"SensitiveStr\":123"); + } + + MemoryStream mutated = new(Encoding.UTF8.GetBytes(jsonText)); + + // Act + MemoryStream output = new(); + DecryptionContext ctx = await new StreamProcessor().DecryptStreamAsync(mutated, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + + // Assert: value remains number and path is not recorded as decrypted + output.Position = 0; + using JsonDocument outDoc = JsonDocument.Parse(output); + Assert.AreEqual(123, outDoc.RootElement.GetProperty("SensitiveStr").GetInt32()); + Assert.IsFalse(ctx.DecryptionInfoList[0].PathsDecrypted.Contains("/SensitiveStr")); + } + + [TestMethod] + public async Task Decrypt_ForgedUnknownTypeMarker_WritesRaw_InvalidJson() + { + // Arrange: create a valid encrypted payload + var doc = new { id = "1", SensitiveStr = "abc" }; + string[] paths = new[] { "/SensitiveStr" }; + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + + // Replace SensitiveStr with a base64 value whose first byte is an unknown type marker (0xEE) + byte[] bogusCipher = new byte[] { 0xEE, 0x01, 0x02, 0x03 }; + string forgedBase64 = Convert.ToBase64String(bogusCipher); + + encrypted.Position = 0; + MemoryStream forged = new(); + using (JsonDocument jd = JsonDocument.Parse(encrypted, new JsonDocumentOptions { AllowTrailingCommas = true })) + using (Utf8JsonWriter w = new(forged)) + { + w.WriteStartObject(); + w.WriteString("id", jd.RootElement.GetProperty("id").GetString()); + w.WriteString("SensitiveStr", forgedBase64); + w.WritePropertyName(Constants.EncryptedInfo); + jd.RootElement.GetProperty(Constants.EncryptedInfo).WriteTo(w); + w.WriteEndObject(); + } + forged.Position = 0; + + // Act + // Use a bypass encryptor to return raw bytes that are not valid JSON, exercising the default branch (WriteRawValue) + StreamProcessor sp = new StreamProcessor { Encryptor = new AlwaysPlaintextMdeEncryptor("NOT_JSON") }; + MemoryStream output = new(); + _ = await sp.DecryptStreamAsync(forged, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + output.Position = 0; + + // Assert: output is not valid JSON due to raw invalid token insertion + try + { + using JsonDocument _ = JsonDocument.Parse(output); + Assert.Fail("Expected JSON parse to fail due to raw invalid token"); + } + catch (Exception ex) + { + // System.Text.Json may throw JsonReaderException (derived) or JsonException depending on runtime + Assert.IsTrue(ex is JsonException, $"Unexpected exception type: {ex.GetType()}"); + } + } + + [TestMethod] + public async Task Decrypt_ForgedTypeMarkerLong_InvalidPayload_Throws() + { + // Arrange: create a valid encrypted payload, then forge the marker to Long while the decryptor returns non-numeric plaintext + var doc = new { id = "1", SensitiveStr = "abc" }; + string[] paths = new[] { "/SensitiveStr" }; + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encrypted, EncryptionProperties props) = await EncryptRawAsync(doc, options); + + byte[] bogusCipher = new byte[] { (byte)TypeMarker.Long, 0xAA, 0xBB, 0xCC }; + string forgedBase64 = Convert.ToBase64String(bogusCipher); + + encrypted.Position = 0; + MemoryStream forged = new(); + using (JsonDocument jd = JsonDocument.Parse(encrypted, new JsonDocumentOptions { AllowTrailingCommas = true })) + using (Utf8JsonWriter w = new(forged)) + { + w.WriteStartObject(); + w.WriteString("id", jd.RootElement.GetProperty("id").GetString()); + w.WriteString("SensitiveStr", forgedBase64); + w.WritePropertyName(Constants.EncryptedInfo); + jd.RootElement.GetProperty(Constants.EncryptedInfo).WriteTo(w); + w.WriteEndObject(); + } + forged.Position = 0; + + // Act + // Use encryptor that returns a plaintext that is invalid for a long serializer + StreamProcessor sp = new StreamProcessor { Encryptor = new AlwaysPlaintextMdeEncryptor("abc") }; + MemoryStream output = new(); + try + { + await sp.DecryptStreamAsync(forged, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + Assert.Fail("Expected exception due to invalid bigint serializer input"); + } + catch (Exception ex) + { + // Serializer throws ArgumentSizeIncorrectException; verify something was thrown + Assert.IsTrue(ex != null); + } + } + + [TestMethod] + public async Task Decrypt_Fuzz_Ciphertext_Length_And_TypeMarker_CrossProduct() + { + // Arrange + // Property-style fuzzing across type markers and plaintext lengths. We don't assert per-iteration outcomes; + // instead we ensure a wide set runs without catastrophic failures and that some known-good cases succeed. + var doc = new { id = "1", V = "seed" }; + string[] paths = new[] { "/V" }; + EncryptionOptions options = CreateOptions(paths); + (MemoryStream encryptedSeed, EncryptionProperties props) = await EncryptRawAsync(doc, options); + + // Extract _ei once to reuse in forged documents + string eiRaw; + string idValue; + encryptedSeed.Position = 0; + using (JsonDocument jd = JsonDocument.Parse(encryptedSeed, new JsonDocumentOptions { AllowTrailingCommas = true })) + { + idValue = jd.RootElement.GetProperty("id").GetString(); + eiRaw = jd.RootElement.GetProperty(Constants.EncryptedInfo).GetRawText(); + } + + Random rng = new Random(1234); + byte[] markers = new byte[] { (byte)TypeMarker.String, (byte)TypeMarker.Long, (byte)TypeMarker.Double, (byte)TypeMarker.Boolean, 0xEE /* unknown */ }; + int iterationsPerLen = 4; + int maxLen = 16; + int attempts = 0; + int successes = 0; + + StreamProcessor sp = new StreamProcessor { Encryptor = new MutablePlaintextMdeEncryptor() }; + MutablePlaintextMdeEncryptor mut = (MutablePlaintextMdeEncryptor)sp.Encryptor; + + // Act + for (int len = 0; len <= maxLen; len++) + { + for (int m = 0; m < markers.Length; m++) + { + for (int i = 0; i < iterationsPerLen; i++) + { + attempts++; + byte marker = markers[m]; + byte[] plain = new byte[len]; + rng.NextBytes(plain); + mut.Payload = plain; + + // Build forged ciphertext (type marker only matters to switch in decryptor) + byte[] cipher = new byte[1 + 1]; // marker + 1 byte minimal to base64 properly + cipher[0] = marker; + cipher[1] = 0x00; + string base64 = Convert.ToBase64String(cipher); + + using MemoryStream forged = new(); + using (Utf8JsonWriter w = new(forged)) + { + w.WriteStartObject(); + w.WriteString("id", idValue); + w.WriteString("V", base64); + w.WritePropertyName(Constants.EncryptedInfo); + using (JsonDocument eiDoc = JsonDocument.Parse(eiRaw)) + { + eiDoc.RootElement.WriteTo(w); + } + w.WriteEndObject(); + } + forged.Position = 0; + + try + { + using MemoryStream output = new(); + _ = await sp.DecryptStreamAsync(forged, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + successes++; + } + catch + { + // Expected for many combinations (e.g., size mismatch or invalid UTF-8); continue + } + } + } + } + + // Add a few known-good shapes that should succeed to guarantee coverage of successful serialization paths + (byte marker, byte[] payload)[] knownGood = new (byte marker, byte[] payload)[] + { + ((byte)TypeMarker.String, Encoding.UTF8.GetBytes("ok")), + ((byte)TypeMarker.Long, new byte[8] /* 0L */), + ((byte)TypeMarker.Double, new byte[8] /* 0.0 */), + ((byte)TypeMarker.Boolean, new byte[]{ 1 }), + }; + foreach ((byte marker, byte[] payload) in knownGood) + { + attempts++; + mut.Payload = payload; + string base64 = Convert.ToBase64String(new byte[] { marker, 0x00 }); + using MemoryStream forged = new(); + using (Utf8JsonWriter w = new(forged)) + { + w.WriteStartObject(); + w.WriteString("id", idValue); + w.WriteString("V", base64); + w.WritePropertyName(Constants.EncryptedInfo); + using (JsonDocument eiDoc = JsonDocument.Parse(eiRaw)) + { + eiDoc.RootElement.WriteTo(w); + } + w.WriteEndObject(); + } + forged.Position = 0; + using MemoryStream output = new(); + try + { + _ = await sp.DecryptStreamAsync(forged, output, mockEncryptor.Object, props, new CosmosDiagnosticsContext(), CancellationToken.None); + successes++; + } + catch + { + // Some environments may still throw for Double if writer rejects NaN/Inf, but 0.0 should be fine; ignore either way + } + } + + // Assert + Assert.IsTrue(attempts > 0, "No fuzz attempts executed"); + Assert.IsTrue(successes > 0, "Expected at least some successful decrypt/writes during fuzzing"); + } + + // Note: JsonTokenType.Comment branch remains uncovered intentionally. The decryptor configures JsonReaderOptions with CommentHandling.Skip (readonly static), + // and the encryption pipeline never emits comments. Altering the static readonly field or constructing a custom reader just for coverage would add fragility. + // The switch case exists defensively; functional risk is negligible. + + private class NullMarkerMdeEncryptor : MdeEncryptor + { + internal override (byte[] plainText, int plainTextLength) Decrypt(DataEncryptionKey encryptionKey, byte[] cipherText, int cipherTextLength, ArrayPoolManager arrayPoolManager) + { + if ((TypeMarker)cipherText[0] == TypeMarker.Null) + { + // Return empty plaintext buffer (length 0). Caller will write null based on marker (already in cipherText[0]). + byte[] buffer = arrayPoolManager.Rent(0); + return (buffer, 0); + } + + // Delegate to normal decrypt logic for non-null markers so compression scenarios work. + return base.Decrypt(encryptionKey, cipherText, cipherTextLength, arrayPoolManager); + } + } + + private class AlwaysPlaintextMdeEncryptor : MdeEncryptor + { + private readonly byte[] payload; + + public AlwaysPlaintextMdeEncryptor(string raw) + { + this.payload = Encoding.UTF8.GetBytes(raw); + } + + internal override (byte[] plainText, int plainTextLength) Decrypt(DataEncryptionKey encryptionKey, byte[] cipherText, int cipherTextLength, ArrayPoolManager arrayPoolManager) + { + byte[] buffer = arrayPoolManager.Rent(this.payload.Length); + this.payload.AsSpan().CopyTo(buffer); + return (buffer, this.payload.Length); + } + } + + private class MutablePlaintextMdeEncryptor : MdeEncryptor + { + public byte[] Payload { get; set; } = Array.Empty(); + + internal override (byte[] plainText, int plainTextLength) Decrypt(DataEncryptionKey encryptionKey, byte[] cipherText, int cipherTextLength, ArrayPoolManager arrayPoolManager) + { + byte[] buffer = arrayPoolManager.Rent(this.Payload.Length); + if (this.Payload.Length > 0) + { + this.Payload.AsSpan().CopyTo(buffer); + } + return (buffer, this.Payload.Length); + } + } + + } +} +#endif diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/StreamProcessorEncryptorTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/StreamProcessorEncryptorTests.cs new file mode 100644 index 0000000000..b798f93883 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Transformation/StreamProcessorEncryptorTests.cs @@ -0,0 +1,550 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +#if NET8_0_OR_GREATER +namespace Microsoft.Azure.Cosmos.Encryption.Tests.Transformation +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using System.Text.Json; + using System.Globalization; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Encryption.Custom; + using Microsoft.Azure.Cosmos.Encryption.Custom.Transformation; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + + [TestClass] + public class StreamProcessorEncryptorTests + { + private const string DekId = "dekId"; + private static Mock mockEncryptor; + private static Mock mockDek; + + [ClassInitialize] + public static void Init(TestContext ctx) + { + _ = ctx; + StreamProcessor.InitialBufferSize = 8; // exercise buffer growth + + mockEncryptor = TestEncryptorFactory.CreateMde(DekId, out mockDek); + } + + private static EncryptionOptions CreateOptions(IEnumerable paths) + { + return new EncryptionOptions + { + DataEncryptionKeyId = DekId, + EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized, + PathsToEncrypt = paths.ToList() + }; + } + + private static async Task EncryptAsync(object doc, EncryptionOptions options) + { + Stream input = TestCommon.ToStream(doc); + MemoryStream output = new(); + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + output.Position = 0; + return output; + } + + private static JsonDocument Parse(Stream s) + { + s.Position = 0; + return JsonDocument.Parse(s); + } + + [TestMethod] + public async Task Encrypt_AllPrimitiveTypesAndContainers() + { + // Arrange + var doc = new + { + id = Guid.NewGuid().ToString(), + SensitiveStr = "abc", + SensitiveInt = 123L, // long branch + SensitiveDouble = 3.14159, + SensitiveBoolTrue = true, + SensitiveBoolFalse = false, + SensitiveNull = (string)null, // will not be encrypted + SensitiveArr = new object[] { 1, 2, 3 }, + SensitiveObj = new { a = 5, b = "text" }, + NonSensitive = 42 + }; + string[] paths = new[] { "/SensitiveStr", "/SensitiveInt", "/SensitiveDouble", "/SensitiveBoolTrue", "/SensitiveBoolFalse", "/SensitiveNull", "/SensitiveArr", "/SensitiveObj" }; + EncryptionOptions options = CreateOptions(paths); + + // Act (encrypt) + MemoryStream encrypted = await EncryptAsync(doc, options); + using JsonDocument jd = Parse(encrypted); + JsonElement root = jd.RootElement; + + // Assert (encryption) + // Encryption properties appended + Assert.IsTrue(root.TryGetProperty(Constants.EncryptedInfo, out JsonElement ei)); + EncryptionProperties props = System.Text.Json.JsonSerializer.Deserialize(ei.GetRawText()); + Assert.AreEqual(EncryptionFormatVersion.Mde, props.EncryptionFormatVersion); + + // Null path should be excluded + Assert.IsFalse(props.EncryptedPaths.Contains("/SensitiveNull")); + + foreach (string path in paths.Where(p => p != "/SensitiveNull")) + { + string name = path.TrimStart('/'); + string base64 = root.GetProperty(name).GetString(); + Assert.IsNotNull(base64); + byte[] cipherBytes = Convert.FromBase64String(base64); + // first byte is type marker + switch (name) + { + case "SensitiveStr": Assert.AreEqual((byte)TypeMarker.String, cipherBytes[0]); break; + case "SensitiveInt": Assert.AreEqual((byte)TypeMarker.Long, cipherBytes[0]); break; + case "SensitiveDouble": Assert.AreEqual((byte)TypeMarker.Double, cipherBytes[0]); break; + case "SensitiveBoolTrue": + case "SensitiveBoolFalse": Assert.AreEqual((byte)TypeMarker.Boolean, cipherBytes[0]); break; + case "SensitiveArr": Assert.AreEqual((byte)TypeMarker.Array, cipherBytes[0]); break; + case "SensitiveObj": Assert.AreEqual((byte)TypeMarker.Object, cipherBytes[0]); break; + } + } + + // Act (decrypt) + encrypted.Position = 0; + (Stream decrypted, DecryptionContext ctx) = await EncryptionProcessor.DecryptStreamAsync(encrypted, mockEncryptor.Object, new CosmosDiagnosticsContext(), CancellationToken.None); + // Assert (roundtrip) + using JsonDocument d2 = Parse(decrypted); + JsonElement r2 = d2.RootElement; + Assert.AreEqual(doc.SensitiveStr, r2.GetProperty("SensitiveStr").GetString()); + Assert.AreEqual((long)doc.SensitiveInt, r2.GetProperty("SensitiveInt").GetInt64()); + Assert.AreEqual(doc.SensitiveDouble, r2.GetProperty("SensitiveDouble").GetDouble(), 0.00001); + Assert.AreEqual(doc.SensitiveBoolTrue, r2.GetProperty("SensitiveBoolTrue").GetBoolean()); + Assert.AreEqual(doc.SensitiveBoolFalse, r2.GetProperty("SensitiveBoolFalse").GetBoolean()); + Assert.AreEqual(System.Text.Json.JsonValueKind.Null, r2.GetProperty("SensitiveNull").ValueKind); + Assert.AreEqual(3, r2.GetProperty("SensitiveArr").GetArrayLength()); + Assert.AreEqual("text", r2.GetProperty("SensitiveObj").GetProperty("b").GetString()); + Assert.IsTrue(ctx.DecryptionInfoList[0].PathsDecrypted.Contains("/SensitiveStr")); + } + + [TestMethod] + public async Task Encrypt_NestedObjectAndArray() + { + // Arrange + var doc = new + { + id = "1", + Nested = new { a = 5, b = new { c = 10 } }, + Arr = new object[] { new { x = 1 }, new { x = 2 } }, + Plain = 7 + }; + string[] paths = new[] { "/Nested", "/Arr" }; + // Act (encrypt) + MemoryStream encrypted = await EncryptAsync(doc, CreateOptions(paths)); + using JsonDocument jd = Parse(encrypted); + JsonElement root = jd.RootElement; + // Assert (encryption) + Assert.AreEqual(JsonValueKind.String, root.GetProperty("Nested").ValueKind); + Assert.AreEqual(JsonValueKind.String, root.GetProperty("Arr").ValueKind); + + // Act (decrypt) + encrypted.Position = 0; + (Stream decrypted, _) = await EncryptionProcessor.DecryptStreamAsync(encrypted, mockEncryptor.Object, new CosmosDiagnosticsContext(), CancellationToken.None); + // Assert (roundtrip) + using JsonDocument d2 = Parse(decrypted); + JsonElement r2 = d2.RootElement; + Assert.AreEqual(5, r2.GetProperty("Nested").GetProperty("a").GetInt32()); + Assert.AreEqual(2, r2.GetProperty("Arr").GetArrayLength()); + } + + [TestMethod] + public async Task Encrypt_BufferGrowthLargeString() + { + // Arrange + var doc = new { id = "1", Big = new string('a', 5000) }; + string[] paths = new[] { "/Big" }; + // Act + MemoryStream encrypted = await EncryptAsync(doc, CreateOptions(paths)); + using JsonDocument jd = Parse(encrypted); + // Assert + string cipher = jd.RootElement.GetProperty("Big").GetString(); + Assert.IsTrue(cipher.Length > 10); + } + + [TestMethod] + public async Task Encrypt_SkipsNullProperty() + { + // Arrange + var doc = new { id = "1", Maybe = (string)null }; + string[] paths = new[] { "/Maybe" }; + // Act + MemoryStream encrypted = await EncryptAsync(doc, CreateOptions(paths)); + using JsonDocument jd = Parse(encrypted); + JsonElement root = jd.RootElement; + // Assert + EncryptionProperties props = System.Text.Json.JsonSerializer.Deserialize(root.GetProperty(Constants.EncryptedInfo).GetRawText()); + Assert.IsFalse(props.EncryptedPaths.Contains("/Maybe")); + Assert.AreEqual(JsonValueKind.Null, root.GetProperty("Maybe").ValueKind); + } + + [TestMethod] + public async Task Encrypt_NullThenPlain_RemainsPlain() + { + // Arrange + // Regression: if encryption state isn't cleared after a null, the next property might be incorrectly encrypted + string json = "{\"Maybe\":null,\"Plain\":42,\"id\":\"1\"}"; + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(new[] { "/Maybe" }); + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output); + JsonElement root = jd.RootElement; + // Assert + // Maybe remains null and is not listed in encrypted paths + Assert.AreEqual(JsonValueKind.Null, root.GetProperty("Maybe").ValueKind); + EncryptionProperties props = System.Text.Json.JsonSerializer.Deserialize(root.GetProperty(Constants.EncryptedInfo).GetRawText()); + Assert.IsFalse(props.EncryptedPaths.Contains("/Maybe")); + // Plain must remain a number (not a base64 string) + Assert.AreEqual(JsonValueKind.Number, root.GetProperty("Plain").ValueKind); + Assert.AreEqual(42, root.GetProperty("Plain").GetInt32()); + } + + [TestMethod] + public void Encrypt_InternalProperty_Getter_Coverage() + { + // Arrange + // Touch internal partial members for coverage: Encryptor property lives on decryptor partial file + StreamProcessor sp = new StreamProcessor(); + // Assert + Assert.IsNotNull(sp.Encryptor); // covers getter sequence point + Assert.IsTrue(StreamProcessor.InitialBufferSize > 0); + } + + [TestMethod] + public async Task Encrypt_NumberParsing_IsCultureInvariant() + { + // Arrange + // Force a culture that expects comma as decimal separator so parsing a dot-formatted invariant number fails + CultureInfo original = CultureInfo.CurrentCulture; + CultureInfo originalUi = CultureInfo.CurrentUICulture; + try + { + CultureInfo commaCulture = new CultureInfo("fr-FR"); + CultureInfo.CurrentCulture = commaCulture; + CultureInfo.CurrentUICulture = commaCulture; + + // Anonymous object with a decimal value will be serialized using invariant culture ("1.23") by Json.NET + var doc = new { id = "1", Weird = 1.23m }; + string[] paths = new[] { "/Weird" }; + EncryptionOptions options = CreateOptions(paths); + + // Act + // Should succeed regardless of current culture and round-trip the value as a double + MemoryStream encrypted = await EncryptAsync(doc, options); + encrypted.Position = 0; + (Stream decrypted, _) = await EncryptionProcessor.DecryptStreamAsync(encrypted, mockEncryptor.Object, new CosmosDiagnosticsContext(), CancellationToken.None); + // Assert + using JsonDocument d2 = JsonDocument.Parse(decrypted); + double value = d2.RootElement.GetProperty("Weird").GetDouble(); + Assert.AreEqual(1.23, value, 1e-12); + } + finally + { + CultureInfo.CurrentCulture = original; + CultureInfo.CurrentUICulture = originalUi; + } + } + + [TestMethod] + public async Task Encrypt_InputWithComments_IgnoresComments() + { + // Arrange + // Although StreamProcessor has a comment case, JsonReaderOptions uses CommentHandling=Skip, so comments are not surfaced as tokens. + // This test documents that behavior: comments are silently dropped and encryption still succeeds. + string json = "{\n // comment before sensitive\n \"SensitiveStr\": \"abc\",\n // trailing comment\n \"id\": \"1\"\n}"; + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(new[] { "/SensitiveStr" }); + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + output.Position = 0; + using JsonDocument jd = Parse(output); + JsonElement root = jd.RootElement; + // Assert + // SensitiveStr should be encrypted (string -> base64), id should remain plain, no comments present. + string cipher = root.GetProperty("SensitiveStr").GetString(); + Assert.IsNotNull(cipher); + Assert.AreEqual("1", root.GetProperty("id").GetString()); + // Ensure encrypted info present + Assert.IsTrue(root.TryGetProperty(Constants.EncryptedInfo, out _)); + } + + [TestMethod] + public async Task Encrypt_NonObjectRoot_Array_RemainsUnchanged() + { + // Arrange + // Attacker supplies a non-object root. Encrypt path always appends _ei into an object context, so this must fail. + string json = "[1,2,3]"; + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(Array.Empty()); + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output); + // Assert + Assert.AreEqual(JsonValueKind.Array, jd.RootElement.ValueKind); + } + + [TestMethod] + public async Task Encrypt_NonObjectRoot_Primitive_RemainsUnchanged() + { + // Arrange + foreach (string json in new[] { "123", "\"str\"", "true", "false", "null" }) + { + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(Array.Empty()); + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output); + // Assert + // Ensure the root kind matches the primitive provided + JsonValueKind kind = jd.RootElement.ValueKind; + switch (json) + { + case "123": Assert.AreEqual(JsonValueKind.Number, kind); break; + case "\"str\"": Assert.AreEqual(JsonValueKind.String, kind); break; + case "true": Assert.AreEqual(JsonValueKind.True, kind); break; + case "false": Assert.AreEqual(JsonValueKind.False, kind); break; + case "null": Assert.AreEqual(JsonValueKind.Null, kind); break; + } + } + } + + [TestMethod] + public async Task Encrypt_Fails_OnTruncatedJson() + { + // Arrange + // Missing closing quote and brace + string json = "{\"id\":\"1\",\"SensitiveStr\":\"abc"; // truncated + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(new[] { "/SensitiveStr" }); + try + { + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + // Assert + Assert.Fail("Expected exception for truncated JSON"); + } + catch (Exception ex) when (ex is JsonException || ex is ArgumentOutOfRangeException || ex is InvalidOperationException) + { + // acceptable failure modes + } + } + + [TestMethod] + public async Task Encrypt_Fails_OnDoubleInfinity() + { + // Arrange + // Extremely large exponent overflows to Infinity in double parsing; serializer should not accept it. + string json = "{\"id\":\"1\",\"SensitiveDouble\":1e309}"; + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(new[] { "/SensitiveDouble" }); + try + { + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + // Assert + Assert.Fail("Expected exception for Infinity double serialization"); + } + catch (Exception ex) + { + // Different serializer layers may throw different exception types; verify it's about out-of-range/infinity + StringAssert.Contains(ex.ToString(), "Infinity"); + } + } + + [TestMethod] + public async Task Encrypt_Fails_OnInvalidUtf8InString() + { + // Arrange + // Construct bytes for: {"id":"1","SensitiveStr":""} + // Invalid sequence C3 28 + byte[] bytes = new byte[] { + 0x7B, // { + 0x22, (byte)'i', (byte)'d', 0x22, 0x3A, 0x22, (byte)'1', 0x22, 0x2C, + 0x22, (byte)'S', (byte)'e', (byte)'n', (byte)'s', (byte)'i', (byte)'t', (byte)'i', (byte)'v', (byte)'e', (byte)'S', (byte)'t', (byte)'r', 0x22, 0x3A, 0x22, + 0xC3, 0x28, // invalid UTF-8 sequence + 0x22, + 0x7D // } + }; + using MemoryStream input = new MemoryStream(bytes); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(new[] { "/SensitiveStr" }); + try + { + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + // Assert + Assert.Fail("Expected parsing failure for invalid UTF-8"); + } + catch (Exception ex) when (ex.GetType().Name.Contains("Json") || ex is InvalidOperationException) + { + // Accept JsonException/JsonReaderException/InvalidOperationException("Cannot transcode invalid UTF-8...") + } + } + + [TestMethod] + public async Task Encrypt_Fails_OnNaN_Literal() + { + // Arrange + // NaN is not valid JSON literal; parsing should fail + string json = "{\"id\":\"1\",\"SensitiveDouble\":NaN}"; + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(new[] { "/SensitiveDouble" }); + try + { + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + // Assert + Assert.Fail("Expected parsing failure for NaN literal"); + } + catch (Exception ex) when (ex.GetType().Name.Contains("Json")) + { + // Accept JsonException/JsonReaderException + } + } + + [TestMethod] + public async Task Encrypt_NegativeZero_Double_RoundtripsAsZero() + { + // Arrange + string json = "{\"id\":\"1\",\"DZ\":-0.0}"; + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream encrypted = new(); + EncryptionOptions options = CreateOptions(new[] { "/DZ" }); + // Act (encrypt) + await EncryptionProcessor.EncryptAsync(input, encrypted, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + encrypted.Position = 0; + using JsonDocument jenc = JsonDocument.Parse(encrypted); + byte[] cipher = Convert.FromBase64String(jenc.RootElement.GetProperty("DZ").GetString()); + Assert.AreEqual((byte)TypeMarker.Double, cipher[0]); + + // Act (decrypt) + encrypted.Position = 0; + (Stream decrypted, _) = await EncryptionProcessor.DecryptStreamAsync(encrypted, mockEncryptor.Object, new CosmosDiagnosticsContext(), CancellationToken.None); + // Assert + using JsonDocument jdec = JsonDocument.Parse(decrypted); + Assert.AreEqual(0.0, jdec.RootElement.GetProperty("DZ").GetDouble(), 0.0); + } + + [TestMethod] + public async Task Encrypt_DeepNesting_ExceedsDepth_Fails() + { + // Arrange + // Build deeply nested object under property "Obj" + int depth = 200; + StringBuilder sb = new StringBuilder(); + sb.Append("{\"Obj\":"); + for (int i = 0; i < depth; i++) sb.Append("{"); + sb.Append("\"x\":1"); + for (int i = 0; i < depth; i++) sb.Append("}"); + sb.Append("}"); + string json = sb.ToString(); + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(new[] { "/Obj" }); + try + { + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + // Assert + Assert.Fail("Expected parsing failure for deep nesting"); + } + catch (Exception ex) when (ex.GetType().Name.Contains("Json")) + { + // Accept JsonException/JsonReaderException + } + } + + [TestMethod] + public async Task Encrypt_PathToArray_ButValueIsString_EncryptsAsString() + { + // Arrange + string json = "{\"Arr\":\"not an array\"}"; + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(new[] { "/Arr" }); + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output); + // Assert + string base64 = jd.RootElement.GetProperty("Arr").GetString(); + byte[] cipher = Convert.FromBase64String(base64); + Assert.AreEqual((byte)TypeMarker.String, cipher[0]); + } + + [TestMethod] + public async Task Encrypt_PathToObject_ButValueIsNumber_EncryptsAsNumber() + { + // Arrange + string json = "{\"Obj\":42}"; + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(new[] { "/Obj" }); + // Act + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Stream, new CosmosDiagnosticsContext(), CancellationToken.None); + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output); + // Assert + string base64 = jd.RootElement.GetProperty("Obj").GetString(); + byte[] cipher = Convert.FromBase64String(base64); + Assert.AreEqual((byte)TypeMarker.Long, cipher[0]); + } + + [TestMethod] + public async Task Encrypt_RootArray_NoOpWhenNoPaths() + { + string json = "[ { \"id\": \"1\", \"Secret\": \"abc\" } ]"; + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(Array.Empty()); + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Newtonsoft, new CosmosDiagnosticsContext(), CancellationToken.None); + + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output, new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip }); + Assert.AreEqual(JsonValueKind.Array, jd.RootElement.ValueKind); + string roundTripped = jd.RootElement.GetRawText(); + using JsonDocument expected = JsonDocument.Parse(json); + Assert.AreEqual(expected.RootElement.GetRawText(), roundTripped, "Root array should be preserved when no paths are encrypted."); + } + + [TestMethod] + public async Task Encrypt_PrimitiveRoot_NoOpWhenNoPaths() + { + foreach (string json in new[] { "123", "\"str\"", "true", "false", "null" }) + { + using MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes(json)); + MemoryStream output = new(); + EncryptionOptions options = CreateOptions(Array.Empty()); + await EncryptionProcessor.EncryptAsync(input, output, mockEncryptor.Object, options, JsonProcessor.Newtonsoft, new CosmosDiagnosticsContext(), CancellationToken.None); + + output.Position = 0; + using JsonDocument jd = JsonDocument.Parse(output); + using JsonDocument expected = JsonDocument.Parse(json); + Assert.AreEqual(expected.RootElement.ValueKind, jd.RootElement.ValueKind, $"Primitive root {json} should be preserved."); + } + } + } +} +#endif diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Utils/ArgumentValidationTests.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Utils/ArgumentValidationTests.cs new file mode 100644 index 0000000000..3b70441fac --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Tests/Utils/ArgumentValidationTests.cs @@ -0,0 +1,663 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Custom.Tests.Utils +{ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class ArgumentValidationTests + { + #region ThrowIfNull Tests + + [TestMethod] + public void ThrowIfNull_WithNullArgument_ThrowsArgumentNullException() + { + // Arrange + object nullObject = null; + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNull(nullObject, "testParam")); + + Assert.AreEqual("testParam", ex.ParamName); + } + + [TestMethod] + public void ThrowIfNull_WithNonNullArgument_DoesNotThrow() + { + // Arrange + object nonNullObject = new object(); + + // Act & Assert - should not throw + ArgumentValidation.ThrowIfNull(nonNullObject, "testParam"); + } + + [TestMethod] + public void ThrowIfNull_WithNullArgumentAndNoParamName_ThrowsArgumentNullException() + { + // Arrange + object nullObject = null; + + // Act & Assert + Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNull(nullObject)); + } + + #endregion + + #region ThrowIfNullOrEmpty Tests + + [TestMethod] + public void ThrowIfNullOrEmpty_WithNullString_ThrowsArgumentNullException() + { + // Arrange + string nullString = null; + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNullOrEmpty(nullString, "testParam")); + + Assert.AreEqual("testParam", ex.ParamName); + } + + [TestMethod] + public void ThrowIfNullOrEmpty_WithEmptyString_ThrowsArgumentException() + { + // Arrange + string emptyString = string.Empty; + + // Act & Assert + ArgumentException ex = Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNullOrEmpty(emptyString, "testParam")); + + Assert.AreEqual("testParam", ex.ParamName); + Assert.IsTrue(ex.Message.Contains("cannot be an empty string") || ex.Message.Contains("empty")); + } + + [TestMethod] + public void ThrowIfNullOrEmpty_WithValidString_DoesNotThrow() + { + // Arrange + string validString = "validValue"; + + // Act & Assert - should not throw + ArgumentValidation.ThrowIfNullOrEmpty(validString, "testParam"); + } + + [TestMethod] + public void ThrowIfNullOrEmpty_WithWhitespaceString_DoesNotThrow() + { + // Arrange + string whitespaceString = " "; + + // Act & Assert - should not throw (ThrowIfNullOrEmpty allows whitespace) + ArgumentValidation.ThrowIfNullOrEmpty(whitespaceString, "testParam"); + } + + #endregion + + #region ThrowIfNullOrWhiteSpace Tests + + [TestMethod] + public void ThrowIfNullOrWhiteSpace_WithNullString_ThrowsArgumentNullException() + { + // Arrange + string nullString = null; + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNullOrWhiteSpace(nullString, "testParam")); + + Assert.AreEqual("testParam", ex.ParamName); + } + + [TestMethod] + public void ThrowIfNullOrWhiteSpace_WithEmptyString_ThrowsArgumentException() + { + // Arrange + string emptyString = string.Empty; + + // Act & Assert + ArgumentException ex = Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNullOrWhiteSpace(emptyString, "testParam")); + + Assert.AreEqual("testParam", ex.ParamName); + } + + [TestMethod] + public void ThrowIfNullOrWhiteSpace_WithWhitespaceString_ThrowsArgumentException() + { + // Arrange + string whitespaceString = " "; + + // Act & Assert + ArgumentException ex = Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNullOrWhiteSpace(whitespaceString, "testParam")); + + Assert.AreEqual("testParam", ex.ParamName); + Assert.IsTrue(ex.Message.Contains("whitespace") || ex.Message.Contains("empty")); + } + + [TestMethod] + public void ThrowIfNullOrWhiteSpace_WithTabAndNewlineString_ThrowsArgumentException() + { + // Arrange + string whitespaceString = "\t\n\r"; + + // Act & Assert + ArgumentException ex = Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNullOrWhiteSpace(whitespaceString, "testParam")); + + Assert.AreEqual("testParam", ex.ParamName); + } + + [TestMethod] + public void ThrowIfNullOrWhiteSpace_WithValidString_DoesNotThrow() + { + // Arrange + string validString = "validValue"; + + // Act & Assert - should not throw + ArgumentValidation.ThrowIfNullOrWhiteSpace(validString, "testParam"); + } + + [TestMethod] + public void ThrowIfNullOrWhiteSpace_WithStringContainingWhitespace_DoesNotThrow() + { + // Arrange + string validString = " valid "; + + // Act & Assert - should not throw (contains non-whitespace characters) + ArgumentValidation.ThrowIfNullOrWhiteSpace(validString, "testParam"); + } + + #endregion + + #region ThrowIfNegative Tests + + [TestMethod] + public void ThrowIfNegative_WithNegativeValue_ThrowsArgumentOutOfRangeException() + { + // Arrange + int negativeValue = -1; + + // Act & Assert + ArgumentOutOfRangeException ex = Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNegative(negativeValue, "testParam")); + + Assert.AreEqual("testParam", ex.ParamName); + Assert.IsTrue(ex.Message.Contains("non-negative") || ex.Message.Contains("negative")); + } + + [TestMethod] + public void ThrowIfNegative_WithLargeNegativeValue_ThrowsArgumentOutOfRangeException() + { + // Arrange + int negativeValue = int.MinValue; + + // Act & Assert + ArgumentOutOfRangeException ex = Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNegative(negativeValue, "testParam")); + + Assert.AreEqual("testParam", ex.ParamName); + } + + [TestMethod] + public void ThrowIfNegative_WithZero_DoesNotThrow() + { + // Arrange + int zeroValue = 0; + + // Act & Assert - should not throw + ArgumentValidation.ThrowIfNegative(zeroValue, "testParam"); + } + + [TestMethod] + public void ThrowIfNegative_WithPositiveValue_DoesNotThrow() + { + // Arrange + int positiveValue = 42; + + // Act & Assert - should not throw + ArgumentValidation.ThrowIfNegative(positiveValue, "testParam"); + } + + [TestMethod] + public void ThrowIfNegative_WithMaxValue_DoesNotThrow() + { + // Arrange + int maxValue = int.MaxValue; + + // Act & Assert - should not throw + ArgumentValidation.ThrowIfNegative(maxValue, "testParam"); + } + + #endregion + + #region ThrowIfGreaterThan Tests + + [TestMethod] + public void ThrowIfGreaterThan_WithValueGreaterThanOther_ThrowsArgumentOutOfRangeException() + { + // Arrange + int value = 10; + int other = 5; + + // Act & Assert + ArgumentOutOfRangeException ex = Assert.ThrowsException(() => + ArgumentValidation.ThrowIfGreaterThan(value, other, "testParam")); + + Assert.AreEqual("testParam", ex.ParamName); + Assert.IsTrue(ex.Message.Contains("less than or equal") || ex.Message.Contains("greater")); + } + + [TestMethod] + public void ThrowIfGreaterThan_WithValueEqualToOther_DoesNotThrow() + { + // Arrange + int value = 5; + int other = 5; + + // Act & Assert - should not throw + ArgumentValidation.ThrowIfGreaterThan(value, other, "testParam"); + } + + [TestMethod] + public void ThrowIfGreaterThan_WithValueLessThanOther_DoesNotThrow() + { + // Arrange + int value = 3; + int other = 5; + + // Act & Assert - should not throw + ArgumentValidation.ThrowIfGreaterThan(value, other, "testParam"); + } + + [TestMethod] + public void ThrowIfGreaterThan_WithNegativeValues_WorksCorrectly() + { + // Arrange & Act & Assert - should throw + Assert.ThrowsException(() => + ArgumentValidation.ThrowIfGreaterThan(-1, -5, "testParam")); + + // Should not throw + ArgumentValidation.ThrowIfGreaterThan(-10, -5, "testParam"); + ArgumentValidation.ThrowIfGreaterThan(-5, -5, "testParam"); + } + + [TestMethod] + public void ThrowIfGreaterThan_WithBoundaryValues_WorksCorrectly() + { + // Arrange & Act & Assert + // Should throw when value > other + Assert.ThrowsException(() => + ArgumentValidation.ThrowIfGreaterThan(int.MaxValue, int.MaxValue - 1, "testParam")); + + // Should not throw when value <= other + ArgumentValidation.ThrowIfGreaterThan(int.MinValue, int.MaxValue, "testParam"); + ArgumentValidation.ThrowIfGreaterThan(int.MaxValue, int.MaxValue, "testParam"); + } + + #endregion + + #region Edge Cases and Integration Tests + + [TestMethod] + public void AllMethods_WithNullParamName_DoNotThrow() + { + // Test that all methods work without param name + ArgumentValidation.ThrowIfNull(new object()); + ArgumentValidation.ThrowIfNullOrEmpty("valid"); + ArgumentValidation.ThrowIfNullOrWhiteSpace("valid"); + ArgumentValidation.ThrowIfNegative(0); + ArgumentValidation.ThrowIfGreaterThan(5, 10); + } + + [TestMethod] + public void ThrowIfNull_WithDifferentTypes_WorksCorrectly() + { + // Test with various reference types + ArgumentValidation.ThrowIfNull("string", "param1"); + ArgumentValidation.ThrowIfNull(new int[] { 1, 2, 3 }, "param2"); + ArgumentValidation.ThrowIfNull(new ArgumentException(), "param3"); + + // Test that null of different types throws + Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNull((string)null, "param1")); + Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNull((int[])null, "param2")); + Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNull((Exception)null, "param3")); + } + + [TestMethod] + public void StringValidations_WithUnicodeCharacters_WorkCorrectly() + { + // Valid Unicode string should not throw + ArgumentValidation.ThrowIfNullOrEmpty("Hello 世界"); + ArgumentValidation.ThrowIfNullOrWhiteSpace("こんにちは"); + + // Unicode whitespace should be caught + string unicodeWhitespace = "\u00A0\u2003"; // Non-breaking space and em space + Assert.ThrowsException(() => + ArgumentValidation.ThrowIfNullOrWhiteSpace(unicodeWhitespace, "testParam")); + } + + #endregion + + #region Behavioral Verification Tests - Proves Validation Actually Works + + [TestMethod] + public void ThrowIfNull_ActuallyPreventsNullFromBeingUsed() + { + // This test proves that the validation actually prevents null usage + // not just that it throws an exception + + string testValue = null; + bool validationWorked = false; + + try + { + ArgumentValidation.ThrowIfNull(testValue, nameof(testValue)); + // If we get here, validation failed - null was not caught + Assert.Fail("Validation should have thrown ArgumentNullException for null value"); + } + catch (ArgumentNullException ex) + { + // Validation worked - verify the exception details are correct + Assert.AreEqual(nameof(testValue), ex.ParamName); + validationWorked = true; + } + + Assert.IsTrue(validationWorked, "Validation must throw ArgumentNullException for null values"); + } + + [TestMethod] + public void ThrowIfNullOrEmpty_ActuallyDistinguishesBetweenNullAndEmpty() + { + // Verify that null throws ArgumentNullException (not ArgumentException) + string nullValue = null; + try + { + ArgumentValidation.ThrowIfNullOrEmpty(nullValue, "nullValue"); + Assert.Fail("Should have thrown ArgumentNullException for null"); + } + catch (ArgumentNullException ex) + { + // Correct - null should throw ArgumentNullException + Assert.AreEqual("nullValue", ex.ParamName); + Assert.IsFalse(ex is ArgumentException && ex is not ArgumentNullException, + "Null should throw ArgumentNullException, not ArgumentException"); + } + + // Verify that empty throws ArgumentException (not ArgumentNullException) + string emptyValue = string.Empty; + try + { + ArgumentValidation.ThrowIfNullOrEmpty(emptyValue, "emptyValue"); + Assert.Fail("Should have thrown ArgumentException for empty string"); + } + catch (ArgumentException ex) when (ex is not ArgumentNullException) + { + // Correct - empty should throw ArgumentException but not ArgumentNullException + Assert.AreEqual("emptyValue", ex.ParamName); + } + catch (ArgumentNullException) + { + Assert.Fail("Empty string should throw ArgumentException, not ArgumentNullException"); + } + } + + [TestMethod] + public void ThrowIfNullOrWhiteSpace_ActuallyValidatesWhitespace() + { + // Verify various whitespace characters are detected + string[] whitespaceStrings = new[] + { + " ", // Single space + " ", // Multiple spaces + "\t", // Tab + "\n", // Newline + "\r", // Carriage return + "\r\n", // Windows line ending + " \t\n\r ", // Mixed whitespace + "\u00A0", // Non-breaking space + "\u2003" // Em space + }; + + foreach (string whitespace in whitespaceStrings) + { + try + { + ArgumentValidation.ThrowIfNullOrWhiteSpace(whitespace, "whitespace"); + Assert.Fail($"Whitespace '{whitespace.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t")}' should have been caught"); + } + catch (ArgumentException ex) + { + // Correct - whitespace was caught + Assert.AreEqual("whitespace", ex.ParamName); + } + } + + // Verify that strings with actual content pass + string[] validStrings = new[] + { + "a", + " a ", // Content with surrounding whitespace + "hello", + "hello world", + "123" + }; + + foreach (string valid in validStrings) + { + // Should not throw + ArgumentValidation.ThrowIfNullOrWhiteSpace(valid, "valid"); + } + } + + [TestMethod] + public void ThrowIfNegative_ActuallyChecksSign() + { + // Test boundary between negative and non-negative + int justNegative = -1; + int zero = 0; + int justPositive = 1; + + // -1 should throw + try + { + ArgumentValidation.ThrowIfNegative(justNegative, "negative"); + Assert.Fail("-1 should be considered negative"); + } + catch (ArgumentOutOfRangeException ex) + { + Assert.AreEqual("negative", ex.ParamName); + Assert.AreEqual(justNegative, ex.ActualValue); + } + + // 0 should NOT throw (zero is non-negative) + ArgumentValidation.ThrowIfNegative(zero, "zero"); + + // 1 should NOT throw + ArgumentValidation.ThrowIfNegative(justPositive, "positive"); + + // Test extreme values + try + { + ArgumentValidation.ThrowIfNegative(int.MinValue, "minValue"); + Assert.Fail("int.MinValue should be considered negative"); + } + catch (ArgumentOutOfRangeException ex) + { + Assert.AreEqual("minValue", ex.ParamName); + } + + ArgumentValidation.ThrowIfNegative(int.MaxValue, "maxValue"); + } + + [TestMethod] + public void ThrowIfGreaterThan_ActuallyComparesValues() + { + // Test exact boundary conditions + int value = 10; + int lowerBound = 5; + int equalBound = 10; + int higherBound = 15; + + // value > lowerBound should throw (10 > 5) + try + { + ArgumentValidation.ThrowIfGreaterThan(value, lowerBound, "value"); + Assert.Fail("10 > 5 should throw"); + } + catch (ArgumentOutOfRangeException ex) + { + Assert.AreEqual("value", ex.ParamName); + Assert.AreEqual(value, ex.ActualValue); + Assert.IsTrue(ex.Message.Contains(lowerBound.ToString()), + "Exception message should mention the comparison value"); + } + + // value == equalBound should NOT throw (10 <= 10) + ArgumentValidation.ThrowIfGreaterThan(value, equalBound, "value"); + + // value < higherBound should NOT throw (10 < 15) + ArgumentValidation.ThrowIfGreaterThan(value, higherBound, "value"); + } + + [TestMethod] + public void ValidationMethods_ProveTheyDontAcceptInvalidValues() + { + // This test proves that validation methods reject invalid inputs + // and accept valid inputs - testing the actual contract + + int invalidCallsBlocked = 0; + int validCallsAllowed = 0; + + // ThrowIfNull - should block null (1 invalid) + try + { + ArgumentValidation.ThrowIfNull(null, "test"); + Assert.Fail("ThrowIfNull should have thrown for null"); + } + catch (ArgumentNullException) + { + invalidCallsBlocked++; + } + + ArgumentValidation.ThrowIfNull("not null", "test"); + validCallsAllowed++; + + // ThrowIfNullOrEmpty - should block null and empty (2 invalid) + try + { + ArgumentValidation.ThrowIfNullOrEmpty(null, "test"); + Assert.Fail("ThrowIfNullOrEmpty should have thrown for null"); + } + catch (ArgumentNullException) + { + invalidCallsBlocked++; + } + + try + { + ArgumentValidation.ThrowIfNullOrEmpty("", "test"); + Assert.Fail("ThrowIfNullOrEmpty should have thrown for empty"); + } + catch (ArgumentException ex) when (ex.GetType() == typeof(ArgumentException)) + { + invalidCallsBlocked++; + } + catch (ArgumentNullException) + { + Assert.Fail("Empty string should throw ArgumentException, not ArgumentNullException"); + } + + ArgumentValidation.ThrowIfNullOrEmpty("valid", "test"); + validCallsAllowed++; + + // ThrowIfNullOrWhiteSpace - should block null, empty, and whitespace (3 invalid) + try + { + ArgumentValidation.ThrowIfNullOrWhiteSpace(null, "test"); + Assert.Fail("ThrowIfNullOrWhiteSpace should have thrown for null"); + } + catch (ArgumentNullException) + { + invalidCallsBlocked++; + } + + try + { + ArgumentValidation.ThrowIfNullOrWhiteSpace("", "test"); + Assert.Fail("ThrowIfNullOrWhiteSpace should have thrown for empty"); + } + catch (ArgumentException ex) when (ex.GetType() == typeof(ArgumentException)) + { + invalidCallsBlocked++; + } + catch (ArgumentNullException) + { + Assert.Fail("Empty string should throw ArgumentException, not ArgumentNullException"); + } + + try + { + ArgumentValidation.ThrowIfNullOrWhiteSpace(" ", "test"); + Assert.Fail("ThrowIfNullOrWhiteSpace should have thrown for whitespace"); + } + catch (ArgumentException ex) when (ex.GetType() == typeof(ArgumentException)) + { + invalidCallsBlocked++; + } + catch (ArgumentNullException) + { + Assert.Fail("Whitespace string should throw ArgumentException, not ArgumentNullException"); + } + + ArgumentValidation.ThrowIfNullOrWhiteSpace("valid", "test"); + validCallsAllowed++; + + // ThrowIfNegative - should block negative (1 invalid) + try + { + ArgumentValidation.ThrowIfNegative(-1, "test"); + Assert.Fail("ThrowIfNegative should have thrown for -1"); + } + catch (ArgumentOutOfRangeException) + { + invalidCallsBlocked++; + } + + ArgumentValidation.ThrowIfNegative(0, "test"); + ArgumentValidation.ThrowIfNegative(1, "test"); + validCallsAllowed += 2; + + // ThrowIfGreaterThan - should block when value > other (1 invalid) + try + { + ArgumentValidation.ThrowIfGreaterThan(10, 5, "test"); + Assert.Fail("ThrowIfGreaterThan should have thrown for 10 > 5"); + } + catch (ArgumentOutOfRangeException) + { + invalidCallsBlocked++; + } + + ArgumentValidation.ThrowIfGreaterThan(5, 5, "test"); + ArgumentValidation.ThrowIfGreaterThan(5, 10, "test"); + validCallsAllowed += 2; + + // Verify all invalid calls were blocked and all valid calls were allowed + // Expected: 1 (null) + 2 (null+empty) + 3 (null+empty+whitespace) + 1 (negative) + 1 (greater) = 8 total invalid + Assert.AreEqual(8, invalidCallsBlocked, "All invalid calls must be blocked"); + Assert.AreEqual(7, validCallsAllowed, "All valid calls must be allowed"); + } + + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainer.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainer.cs index f46282f59c..47f0c9ed8a 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainer.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainer.cs @@ -732,6 +732,21 @@ public override FeedIterator GetItemQueryIterator( } #if ENCRYPTIONPREVIEW +#if SDKPROJECTREF + public override Task SemanticRerankAsync( + string rerankContext, + IEnumerable documents, + IDictionary options = null, + CancellationToken cancellationToken = default) + { + return this.Container.SemanticRerankAsync( + rerankContext, + documents, + options, + cancellationToken); + } + +#endif public override async Task DeleteAllItemsByPartitionKeyStreamAsync( Cosmos.PartitionKey partitionKey, RequestOptions requestOptions = null, @@ -771,23 +786,6 @@ public override Task IsFeedRangePartOfAsync( } #endif -#if SDKPROJECTREF - public override ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes( - string processorName, - ChangeFeedHandler> onChangesDelegate) - { - throw new NotImplementedException(); - } - - public override Task IsFeedRangePartOfAsync( - Cosmos.FeedRange x, - Cosmos.FeedRange y, - CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } -#endif - /// /// This function handles the scenario where a container is deleted(say from different Client) and recreated with same Id but with different client encryption policy. /// The idea is to have the container Rid cached and sent out as part of RequestOptions with Container Rid set in "x-ms-cosmos-intended-collection-rid" header. diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionProcessor.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionProcessor.cs index af42fe338b..74aea001b4 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionProcessor.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionProcessor.cs @@ -23,7 +23,7 @@ internal static class EncryptionProcessor // UTF-8 Encoding private static readonly SqlVarCharSerializer SqlVarcharSerializer = new SqlVarCharSerializer(size: -1, codePageCharacterEncoding: 65001); - private enum TypeMarker : byte + internal enum TypeMarker : byte { Null = 1, // not used Boolean = 2, @@ -222,7 +222,7 @@ await EncryptJTokenAsync( return EncryptionProcessor.BaseSerializer.ToStream(encryptedPropertyValue); } - private static (TypeMarker, byte[]) Serialize(JToken propertyValue) + internal static (TypeMarker, byte[]) Serialize(JToken propertyValue) { return propertyValue.Type switch { @@ -234,7 +234,7 @@ private static (TypeMarker, byte[]) Serialize(JToken propertyValue) }; } - private static JToken DeserializeAndAddProperty( + internal static JToken DeserializeAndAddProperty( byte[] serializedBytes, TypeMarker typeMarker) { @@ -248,11 +248,11 @@ private static JToken DeserializeAndAddProperty( }; } - private static async Task EncryptJTokenAsync( - JToken jTokenToEncrypt, - EncryptionSettingForProperty encryptionSettingForProperty, - bool shouldEscape, - CancellationToken cancellationToken) + internal static async Task EncryptJTokenAsync( + JToken jTokenToEncrypt, + EncryptionSettingForProperty encryptionSettingForProperty, + bool shouldEscape, + CancellationToken cancellationToken) { // Top Level can be an Object if (jTokenToEncrypt.Type == JTokenType.Object) @@ -292,6 +292,63 @@ await EncryptJTokenAsync( return; } + internal static async Task DecryptJTokenAsync( + JToken jTokenToDecrypt, + EncryptionSettingForProperty encryptionSettingForProperty, + bool isEscaped, + CancellationToken cancellationToken) + { + if (jTokenToDecrypt.Type == JTokenType.Object) + { + foreach (JProperty jProperty in jTokenToDecrypt.Children()) + { + await DecryptJTokenAsync( + jProperty.Value, + encryptionSettingForProperty, + isEscaped, + cancellationToken); + } + } + else if (jTokenToDecrypt.Type == JTokenType.Array) + { + if (jTokenToDecrypt.Children().Any()) + { + for (int i = 0; i < jTokenToDecrypt.Count(); i++) + { + await DecryptJTokenAsync( + jTokenToDecrypt[i], + encryptionSettingForProperty, + isEscaped, + cancellationToken); + } + } + } + else + { + jTokenToDecrypt.Replace(await DecryptAndDeserializeValueAsync( + jTokenToDecrypt, + encryptionSettingForProperty, + isEscaped, + cancellationToken)); + } + } + + internal static string ConvertToBase64UriSafeString(byte[] bytesToProcess) + { + string base64String = Convert.ToBase64String(bytesToProcess); + + // Base 64 Encoding with URL and Filename Safe Alphabet https://datatracker.ietf.org/doc/html/rfc4648#section-5 + // https://docs.microsoft.com/en-us/azure/cosmos-db/concepts-limits#per-item-limits, due to base64 conversion and encryption + // the permissible size of the property will further reduce. + return new StringBuilder(base64String, base64String.Length).Replace("/", "_").Replace("+", "-").ToString(); + } + + internal static byte[] ConvertFromBase64UriSafeString(string uriSafeBase64String) + { + StringBuilder fromUriSafeBase64String = new StringBuilder(uriSafeBase64String, uriSafeBase64String.Length).Replace("_", "/").Replace("-", "+"); + return Convert.FromBase64String(fromUriSafeBase64String.ToString()); + } + private static async Task SerializeAndEncryptValueAsync( JToken jTokenToEncrypt, EncryptionSettingForProperty encryptionSettingForProperty, @@ -337,10 +394,10 @@ private static async Task SerializeAndEncryptValueAsync( } private static async Task DecryptAndDeserializeValueAsync( - JToken jToken, - EncryptionSettingForProperty encryptionSettingForProperty, - bool isEscaped, - CancellationToken cancellationToken) + JToken jToken, + EncryptionSettingForProperty encryptionSettingForProperty, + bool isEscaped, + CancellationToken cancellationToken) { byte[] cipherTextWithTypeMarker = null; @@ -379,47 +436,6 @@ private static async Task DecryptAndDeserializeValueAsync( (TypeMarker)cipherTextWithTypeMarker[0]); } - private static async Task DecryptJTokenAsync( - JToken jTokenToDecrypt, - EncryptionSettingForProperty encryptionSettingForProperty, - bool isEscaped, - CancellationToken cancellationToken) - { - if (jTokenToDecrypt.Type == JTokenType.Object) - { - foreach (JProperty jProperty in jTokenToDecrypt.Children()) - { - await DecryptJTokenAsync( - jProperty.Value, - encryptionSettingForProperty, - isEscaped, - cancellationToken); - } - } - else if (jTokenToDecrypt.Type == JTokenType.Array) - { - if (jTokenToDecrypt.Children().Any()) - { - for (int i = 0; i < jTokenToDecrypt.Count(); i++) - { - await DecryptJTokenAsync( - jTokenToDecrypt[i], - encryptionSettingForProperty, - isEscaped, - cancellationToken); - } - } - } - else - { - jTokenToDecrypt.Replace(await DecryptAndDeserializeValueAsync( - jTokenToDecrypt, - encryptionSettingForProperty, - isEscaped, - cancellationToken)); - } - } - private static async Task DecryptObjectAsync( JObject document, EncryptionSettings encryptionSettings, @@ -471,21 +487,5 @@ private static JObject RetrieveItem( return itemJObj; } - - private static string ConvertToBase64UriSafeString(byte[] bytesToProcess) - { - string base64String = Convert.ToBase64String(bytesToProcess); - - // Base 64 Encoding with URL and Filename Safe Alphabet https://datatracker.ietf.org/doc/html/rfc4648#section-5 - // https://docs.microsoft.com/en-us/azure/cosmos-db/concepts-limits#per-item-limits, due to base64 conversion and encryption - // the permissible size of the property will further reduce. - return new StringBuilder(base64String, base64String.Length).Replace("/", "_").Replace("+", "-").ToString(); - } - - private static byte[] ConvertFromBase64UriSafeString(string uriSafeBase64String) - { - StringBuilder fromUriSafeBase64String = new StringBuilder(uriSafeBase64String, uriSafeBase64String.Length).Replace("_", "/").Replace("-", "+"); - return Convert.FromBase64String(fromUriSafeBase64String.ToString()); - } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs index fea4f12ff1..431349c88d 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs @@ -17,6 +17,11 @@ internal sealed class EncryptionSettingForProperty private readonly EncryptionContainer encryptionContainer; + // Test-only hook: when provided, BuildEncryptionAlgorithmForSettingAsync returns this instance + // instead of constructing one via key fetching/unwrapping. This is internal and used only by tests + // through InternalsVisibleTo. + private readonly Microsoft.Data.Encryption.Cryptography.AeadAes256CbcHmac256EncryptionAlgorithm injectedAlgorithm; + public EncryptionSettingForProperty( string clientEncryptionKeyId, Data.Encryption.Cryptography.EncryptionType encryptionType, @@ -29,12 +34,32 @@ public EncryptionSettingForProperty( this.databaseRid = string.IsNullOrEmpty(databaseRid) ? throw new ArgumentNullException(nameof(databaseRid)) : databaseRid; } + // Internal constructor for tests to inject a ready algorithm to enable end-to-end unit testing + // without standing up key providers. Other parameters remain for traceability but are not used + // when an injected algorithm is supplied. + internal EncryptionSettingForProperty( + string clientEncryptionKeyId, + Data.Encryption.Cryptography.EncryptionType encryptionType, + EncryptionContainer encryptionContainer, + string databaseRid, + AeadAes256CbcHmac256EncryptionAlgorithm injectedAlgorithm) + : this(clientEncryptionKeyId, encryptionType, encryptionContainer, databaseRid) + { + this.injectedAlgorithm = injectedAlgorithm ?? throw new ArgumentNullException(nameof(injectedAlgorithm)); + } + public string ClientEncryptionKeyId { get; } public Data.Encryption.Cryptography.EncryptionType EncryptionType { get; } public async Task BuildEncryptionAlgorithmForSettingAsync(CancellationToken cancellationToken) { + // Return the injected algorithm if provided (test-only path) + if (this.injectedAlgorithm != null) + { + return this.injectedAlgorithm; + } + ClientEncryptionKeyProperties clientEncryptionKeyProperties = await this.encryptionContainer.EncryptionCosmosClient.GetClientEncryptionKeyPropertiesAsync( clientEncryptionKeyId: this.ClientEncryptionKeyId, encryptionContainer: this.encryptionContainer, diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettings.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettings.cs index 6f8ebf18b1..972d898b3d 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettings.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettings.cs @@ -16,7 +16,7 @@ internal sealed class EncryptionSettings { private readonly Dictionary encryptionSettingsDictByPropertyName; - private EncryptionSettings(string containerRidValue, IReadOnlyList partitionKeyPath) + internal EncryptionSettings(string containerRidValue, IReadOnlyList partitionKeyPath) { this.ContainerRidValue = containerRidValue; this.PartitionKeyPaths = partitionKeyPath; @@ -38,7 +38,6 @@ public static Task CreateAsync(EncryptionContainer encryptio public EncryptionSettingForProperty GetEncryptionSettingForProperty(string propertyName) { this.encryptionSettingsDictByPropertyName.TryGetValue(propertyName, out EncryptionSettingForProperty encryptionSettingsForProperty); - return encryptionSettingsForProperty; } @@ -51,6 +50,13 @@ public void SetRequestHeaders(RequestOptions requestOptions) }; } + internal void SetEncryptionSettingForProperty( + string propertyName, + EncryptionSettingForProperty encryptionSettingsForProperty) + { + this.encryptionSettingsDictByPropertyName[propertyName] = encryptionSettingsForProperty; + } + private static Data.Encryption.Cryptography.EncryptionType GetEncryptionTypeForProperty(ClientEncryptionIncludedPath clientEncryptionIncludedPath) { return clientEncryptionIncludedPath.EncryptionType switch @@ -135,12 +141,5 @@ await encryptionContainer.EncryptionCosmosClient.GetClientEncryptionKeyPropertie return encryptionSettings; } - - private void SetEncryptionSettingForProperty( - string propertyName, - EncryptionSettingForProperty encryptionSettingsForProperty) - { - this.encryptionSettingsDictByPropertyName[propertyName] = encryptionSettingsForProperty; - } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs b/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs index 0ab32e32a4..c9e54b4509 100644 --- a/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs @@ -3918,8 +3918,8 @@ public override bool Equals(object obj) && this.Sensitive_DoubleFormat == doc.Sensitive_DoubleFormat && this.Sensitive_IntArray == doc.Sensitive_IntArray && this.Sensitive_ObjectArrayType == doc.Sensitive_ObjectArrayType - && this.Sensitive_NestedObjectFormatL1 != doc.Sensitive_NestedObjectFormatL1 - && this.Sensitive_ArrayMultiTypes != doc.Sensitive_ArrayMultiTypes; + && this.Sensitive_NestedObjectFormatL1 == doc.Sensitive_NestedObjectFormatL1 + && this.Sensitive_ArrayMultiTypes == doc.Sensitive_ArrayMultiTypes; } public bool EqualsExceptEncryptedProperty(object obj) diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/ContractEnforcementTests.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/ContractEnforcementTests.cs index 85d2d1715e..5dd3a4ace6 100644 --- a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/ContractEnforcementTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/ContractEnforcementTests.cs @@ -1,20 +1,32 @@ namespace Microsoft.Azure.Cosmos.Encryption.Tests { - using System; using Microsoft.VisualStudio.TestTools.UnitTesting; - + [TestCategory("Windows")] [TestCategory("UpdateContract")] [TestClass] public class ContractEnforcementTests { + /// + /// This test validates the public API surface against a baseline contract. + /// + /// IMPORTANT: Because tests run on multiple .NET versions (net6.0 and net8.0), + /// the contract validation uses framework-specific baselines to ensure consistency: + /// + /// - When running on net6.0: validates against DotNetSDKEncryptionAPI.net6.json + /// - When running on net8.0: validates against DotNetSDKEncryptionAPI.net8.json + /// + /// To update baselines, run: UpdateContracts.ps1 from the repository root. + /// This script runs tests on BOTH net6.0 and net8.0 to generate both baselines. + /// [TestMethod] public void ContractChanges() { - Cosmos.Tests.Contracts.ContractEnforcement.ValidateContractContainBreakingChanges( + Cosmos.Tests.Contracts.ContractEnforcement.ValidateContract( dllName: "Microsoft.Azure.Cosmos.Encryption", - baselinePath: "DotNetSDKEncryptionAPI.json", - breakingChangesPath: "DotNetSDKEncryptionAPIChanges.json"); + contractType: Cosmos.Tests.Contracts.ContractType.Standard, + baselinePattern: "DotNetSDKEncryptionAPI", + breakingChangesPattern: "DotNetSDKEncryptionAPIChanges"); } } } diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.net6.json b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.net6.json new file mode 100644 index 0000000000..fee8b8e5d9 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.net6.json @@ -0,0 +1,167 @@ +{ + "Subclasses": { + "Microsoft.Azure.Cosmos.Encryption.DataEncryptionAlgorithm;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String AeadAes256CbcHmacSha256": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String AeadAes256CbcHmacSha256;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.FeedIterator Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions.ToEncryptionStreamIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions.ToEncryptionStreamIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.FeedIterator`1[T] Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions.ToEncryptionFeedIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator`1[T] Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions.ToEncryptionFeedIterator[T](Microsoft.Azure.Cosmos.Container, System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.QueryDefinition Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions.CreateQueryDefinition(Microsoft.Azure.Cosmos.Container, System.String)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryDefinition Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions.CreateQueryDefinition(Microsoft.Azure.Cosmos.Container, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Container] Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions.InitializeEncryptionAsync(Microsoft.Azure.Cosmos.Container, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions+))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute", + "ExtensionAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Container] Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions.InitializeEncryptionAsync(Microsoft.Azure.Cosmos.Container, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.EncryptionCosmosClientExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.CosmosClient Microsoft.Azure.Cosmos.Encryption.EncryptionCosmosClientExtensions.WithEncryption(Microsoft.Azure.Cosmos.CosmosClient, Azure.Core.Cryptography.IKeyEncryptionKeyResolver, System.String, System.Nullable`1[System.TimeSpan])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.CosmosClient Microsoft.Azure.Cosmos.Encryption.EncryptionCosmosClientExtensions.WithEncryption(Microsoft.Azure.Cosmos.CosmosClient, Azure.Core.Cryptography.IKeyEncryptionKeyResolver, System.String, System.Nullable`1[System.TimeSpan]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions.CreateClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions+))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute", + "ExtensionAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions.CreateClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions.RewrapClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions+))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute", + "ExtensionAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions.RewrapClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.EncryptionType;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String Deterministic": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Deterministic;IsInitOnly:False;IsStatic:True;" + }, + "System.String Randomized": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Randomized;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.KeyEncryptionKeyResolverName;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String AzureKeyVault": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String AzureKeyVault;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.QueryDefinitionExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.QueryDefinition] Microsoft.Azure.Cosmos.Encryption.QueryDefinitionExtensions.AddParameterAsync(Microsoft.Azure.Cosmos.QueryDefinition, System.String, System.Object, System.String, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.QueryDefinitionExtensions+))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "AsyncStateMachineAttribute", + "ExtensionAttribute" + ], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.QueryDefinition] Microsoft.Azure.Cosmos.Encryption.QueryDefinitionExtensions.AddParameterAsync(Microsoft.Azure.Cosmos.QueryDefinition, System.String, System.Object, System.String, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + } + }, + "Members": { + "Boolean Equals(System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetHashCode()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Boolean System.Object.Equals(System.Object, System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Boolean System.Object.Equals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Boolean System.Object.ReferenceEquals(System.Object, System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Boolean System.Object.ReferenceEquals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String ToString()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.String ToString();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Type GetType()[System.Runtime.CompilerServices.NullableContextAttribute((Byte)1)]-[System.Runtime.CompilerServices.IntrinsicAttribute()]": { + "Type": "Method", + "Attributes": [ + "IntrinsicAttribute", + "NullableContextAttribute" + ], + "MethodInfo": "System.Type GetType();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + } + }, + "NestedTypes": {} +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.json b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.net8.json similarity index 98% rename from Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.json rename to Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.net8.json index 0be417c1eb..44a894134e 100644 --- a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.json +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.net8.json @@ -149,11 +149,10 @@ "Attributes": [], "MethodInfo": "System.String ToString();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Type GetType()[System.Runtime.CompilerServices.NullableContextAttribute((Byte)1)]-[System.Runtime.CompilerServices.IntrinsicAttribute()]": { + "System.Type GetType()[System.Runtime.CompilerServices.IntrinsicAttribute()]": { "Type": "Method", "Attributes": [ - "IntrinsicAttribute", - "NullableContextAttribute" + "IntrinsicAttribute" ], "MethodInfo": "System.Type GetType();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionContainerPatchTests.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionContainerPatchTests.cs new file mode 100644 index 0000000000..66df07678f --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionContainerPatchTests.cs @@ -0,0 +1,78 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Collections.Generic; + using System.Runtime.Serialization; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class EncryptionContainerPatchTests + { + private static T CreateUninitialized() where T : class + { + return (T)FormatterServices.GetUninitializedObject(typeof(T)); + } + + private static EncryptionSettings CreateEncryptionSettingsWithEncryptedProperty(string propertyName, EncryptionContainer container) + { + if (container == null) throw new ArgumentNullException(nameof(container)); + + EncryptionSettings settings = new EncryptionSettings("rid", new List { "/id" }); + EncryptionSettingForProperty forProperty = new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Microsoft.Data.Encryption.Cryptography.EncryptionType.Randomized, + encryptionContainer: container, + databaseRid: "dbRid"); + + settings.SetEncryptionSettingForProperty(propertyName, forProperty); + return settings; + } + + [TestMethod] + public async Task EncryptPatchOperationsAsync_Increment_On_Encrypted_Path_Throws() + { + // Arrange + EncryptionContainer container = CreateUninitialized(); + EncryptionSettings settings = CreateEncryptionSettingsWithEncryptedProperty("Sensitive", container); + List ops = new List + { + PatchOperation.Increment("/Sensitive", 1) + }; + + EncryptionDiagnosticsContext diag = new EncryptionDiagnosticsContext(); + + // Act + Assert + InvalidOperationException ex = await Assert.ThrowsExceptionAsync( + async () => await container.EncryptPatchOperationsAsync(ops, settings, diag, CancellationToken.None)); + + StringAssert.Contains(ex.Message, "Increment patch operation is not allowed for encrypted path"); + StringAssert.Contains(ex.Message, "/Sensitive"); + } + + [TestMethod] + public async Task EncryptPatchOperationsAsync_Increment_On_NonEncrypted_Path_Passes_Through() + { + // Arrange: No encrypted settings for this path + EncryptionContainer container = CreateUninitialized(); + EncryptionSettings settings = CreateEncryptionSettingsWithEncryptedProperty("Sensitive", container); // different property than used below + + PatchOperation op = PatchOperation.Increment("/Plain", 2); + List ops = new List { op }; + EncryptionDiagnosticsContext diag = new EncryptionDiagnosticsContext(); + + // Act + List result = await container.EncryptPatchOperationsAsync(ops, settings, diag, CancellationToken.None); + + // Assert: operation should be passed through unchanged + Assert.AreEqual(1, result.Count); + Assert.AreSame(op, result[0]); + Assert.AreEqual(PatchOperationType.Increment, result[0].OperationType); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionDiagnosticsTests.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionDiagnosticsTests.cs new file mode 100644 index 0000000000..6d3b33cff1 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionDiagnosticsTests.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Runtime.Serialization; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json.Linq; + + [TestClass] + public class EncryptionDiagnosticsTests + { + private static T CreateUninitialized() where T : class + => (T)FormatterServices.GetUninitializedObject(typeof(T)); + + private static EncryptionSettings CreateEncryptionSettingsWithEncryptedProperty(string propertyName, EncryptionContainer container) + { + if (container == null) throw new ArgumentNullException(nameof(container)); + + var settings = new EncryptionSettings("rid", new List { "/id" }); + var forProperty = new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Microsoft.Data.Encryption.Cryptography.EncryptionType.Randomized, + encryptionContainer: container, + databaseRid: "dbRid"); + settings.SetEncryptionSettingForProperty(propertyName, forProperty); + return settings; + } + + private static MemoryStream ToStream(string json) => new MemoryStream(Encoding.UTF8.GetBytes(json)); + + [TestMethod] + public async Task Diagnostics_Encrypt_EndCalled_With_Expected_Count() + { + // Arrange: property exists and is null, so algorithm is not invoked but count still increments + var json = "{\"id\":\"1\",\"Sensitive\":null}"; + using var input = ToStream(json); + + var container = CreateUninitialized(); + var settings = CreateEncryptionSettingsWithEncryptedProperty("Sensitive", container); + var diag = new EncryptionDiagnosticsContext(); + + // Act + using Stream result = await EncryptionProcessor.EncryptAsync(input, settings, diag, CancellationToken.None); + + // Assert + Assert.IsNotNull(result); + + // Verify diagnostics captured properties count + JToken countToken = diag.EncryptContent[Constants.DiagnosticsPropertiesEncryptedCount]; + Assert.IsNotNull(countToken, "Encrypt diagnostics should include properties encrypted count."); + Assert.AreEqual(1, countToken.Value()); + + Assert.IsNotNull(diag.EncryptContent[Constants.DiagnosticsStartTime]); + Assert.IsNotNull(diag.EncryptContent[Constants.DiagnosticsDuration]); + } + + [TestMethod] + public async Task Diagnostics_Decrypt_EndCalled_With_Expected_Count() + { + // Arrange: property exists and is null; decrypt path still visits and counts it + var json = "{\"id\":\"1\",\"Sensitive\":null}"; + using var input = ToStream(json); // MemoryStream is seekable (DEBUG assert satisfied) + + var container = CreateUninitialized(); + var settings = CreateEncryptionSettingsWithEncryptedProperty("Sensitive", container); + var diag = new EncryptionDiagnosticsContext(); + + // Act + using Stream result = await EncryptionProcessor.DecryptAsync(input, settings, diag, CancellationToken.None); + + // Assert + Assert.IsNotNull(result); + + JToken countToken = diag.DecryptContent[Constants.DiagnosticsPropertiesDecryptedCount]; + Assert.IsNotNull(countToken, "Decrypt diagnostics should include properties decrypted count."); + Assert.AreEqual(1, countToken.Value()); + + Assert.IsNotNull(diag.DecryptContent[Constants.DiagnosticsStartTime]); + Assert.IsNotNull(diag.DecryptContent[Constants.DiagnosticsDuration]); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.CoreFunctionality.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.CoreFunctionality.cs new file mode 100644 index 0000000000..74e11038fb --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.CoreFunctionality.cs @@ -0,0 +1,306 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Mde = Microsoft.Data.Encryption.Cryptography; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json.Linq; + using TrackingStream = Microsoft.Azure.Cosmos.Encryption.Tests.TestHelpers.StreamTestHelpers.TrackingStream; + + /// + /// Core functionality tests for EncryptionProcessor including end-to-end encryption/decryption, + /// stream handling, JSON traversal, and main processing flows. + /// + public partial class EncryptionProcessorTests + { + #region End-to-End Tests + + // Removed no-op placeholder test. + + [TestMethod] + public async Task EndToEnd_EncryptDecrypt_RoundTrip_Primitives_And_Arrays_And_Objects() + { + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algorithm = CreateDeterministicAlgorithm(); + + // Configure two properties for encryption: one primitive/array mix, one nested object + EncryptionSettings settings = new EncryptionSettings("rid", new List { "/id" }); + EncryptionContainer container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + + EncryptionSettingForProperty cfg1 = new EncryptionSettingForProperty("cek1", Mde.EncryptionType.Deterministic, container, "dbRid", algorithm); + EncryptionSettingForProperty cfg2 = new EncryptionSettingForProperty("cek2", Mde.EncryptionType.Deterministic, container, "dbRid", algorithm); + + settings.SetEncryptionSettingForProperty("Secret", cfg1); + settings.SetEncryptionSettingForProperty("Nested", cfg2); + + string json = @"{ + ""id"": ""document-id"", + ""Secret"": { ""a"": 1, ""b"": true, ""c"": [ ""x"", 2, false, null, 3.14 ] }, + ""Nested"": { ""inner"": ""value"", ""arr"": [ { ""q"": 42 }, null ] }, + ""Plain"": 123 + }"; + + using (Stream input = ToStream(json)) + { + EncryptionDiagnosticsContext diagEnc = new EncryptionDiagnosticsContext(); + Stream encrypted = await EncryptionProcessor.EncryptAsync(input, settings, diagEnc, CancellationToken.None); + + // Ensure diagnostics counted both properties + Assert.AreEqual(2, diagEnc.EncryptContent[Constants.DiagnosticsPropertiesEncryptedCount].Value()); + + // Decrypt + EncryptionDiagnosticsContext diagDec = new EncryptionDiagnosticsContext(); + Stream decrypted = await EncryptionProcessor.DecryptAsync(encrypted, settings, diagDec, CancellationToken.None); + Assert.AreEqual(2, diagDec.DecryptContent[Constants.DiagnosticsPropertiesDecryptedCount].Value()); + + // Validate round-trip equality + JObject original = JObject.Parse(json); + JObject roundtripped = EncryptionProcessor.BaseSerializer.FromStream(decrypted); + Assert.IsTrue(JToken.DeepEquals(original, roundtripped), "Document should round-trip after encrypt/decrypt."); + } + } + + [TestMethod] + public async Task EndToEnd_EncryptDecrypt_Id_ShouldEscape_And_RoundTrip() + { + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algorithm = CreateDeterministicAlgorithm(); + EncryptionSettings settings = CreateSettingsWithInjected("id", algorithm); + + string id = "id/with+special?chars#and\\slashes"; + // Build the JSON via JObject to ensure proper escaping. + JObject doc = new JObject + { + ["id"] = id, + ["p"] = 1 + }; + + using (Stream input = EncryptionProcessor.BaseSerializer.ToStream(doc)) + { + Stream encrypted = await EncryptionProcessor.EncryptAsync(input, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + + // Inspect encrypted form to ensure id does not contain forbidden characters + JObject encryptedDoc = EncryptionProcessor.BaseSerializer.FromStream(encrypted); + string encId = encryptedDoc.Value("id"); + Assert.IsNotNull(encId); + Assert.IsFalse(encId.Contains('/')); + Assert.IsFalse(encId.Contains('+')); + Assert.IsFalse(encId.Contains('?')); + Assert.IsFalse(encId.Contains('#')); + Assert.IsFalse(encId.Contains('\\')); + + // Decrypt and verify original id restored + Stream decrypted = await EncryptionProcessor.DecryptAsync(EncryptionProcessor.BaseSerializer.ToStream(encryptedDoc), settings, operationDiagnostics: null, CancellationToken.None); + JObject roundtripped = EncryptionProcessor.BaseSerializer.FromStream(decrypted); + Assert.AreEqual(id, roundtripped.Value("id")); + } + } + + #endregion + + #region Stream Handling Tests + + [TestMethod] + public async Task StreamHandling_EncryptAsync_Disposes_Input_And_Returns_New_Stream() + { + // Arrange + TrackingStream input = new TrackingStream(ToStream("{\"id\":\"abc\",\"p\":1}")); + EncryptionSettings settings = CreateSettingsWithNoProperties(); + + // Act + Stream result = await EncryptionProcessor.EncryptAsync(input, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + + // Assert + Assert.IsNotNull(result); + Assert.AreNotSame(input, result); // Should be a different stream + Assert.IsTrue(input.Disposed, "Input stream should be disposed"); + Assert.IsTrue(result.CanRead); + + // Verify content + string content = ReadToEnd(result); + Assert.AreEqual("{\"id\":\"abc\",\"p\":1}", content); + } + + [TestMethod] + public async Task StreamHandling_DecryptAsync_Disposes_Input_And_Returns_New_Stream() + { + // Arrange + TrackingStream input = new TrackingStream(ToStream("{\"id\":\"abc\",\"p\":1}")); + EncryptionSettings settings = CreateSettingsWithNoProperties(); + + // Act + Stream result = await EncryptionProcessor.DecryptAsync(input, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + + // Assert + Assert.IsNotNull(result); + Assert.AreNotSame(input, result); // Should be a different stream + Assert.IsTrue(input.Disposed, "Input stream should be disposed"); + Assert.IsTrue(result.CanRead); + + // Verify content + string content = ReadToEnd(result); + Assert.AreEqual("{\"id\":\"abc\",\"p\":1}", content); + } + + [TestMethod] + public async Task StreamHandling_EncryptDecrypt_NoPropertiesToEncrypt_ReturnsPassthrough() + { + EncryptionSettings settings = CreateSettingsWithNoProperties(); + string originalJson = "{\"id\":\"test\",\"data\":\"value\",\"array\":[1,2,3]}"; + + using (Stream input = ToStream(originalJson)) + { + Stream encrypted = await EncryptionProcessor.EncryptAsync(input, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + + string encryptedJson = ReadToEnd(encrypted); + + using (Stream encryptedInput = ToStream(encryptedJson)) + { + Stream decrypted = await EncryptionProcessor.DecryptAsync(encryptedInput, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + + string decryptedJson = ReadToEnd(decrypted); + + Assert.AreEqual(originalJson, decryptedJson); + } + } + } + + #endregion + + #region JSON Traversal Tests + + private static JToken MakeNestedNullGraph() + { + return JToken.Parse("{ \"a\": null, \"b\": [ null, null, { \"c\": null } ], \"d\": { \"e\": [ null ] } }"); + } + + [TestMethod] + public async Task Traversal_EncryptJTokenAsync_Traverses_Nested_ObjectArray_WithNullLeaves_NoCrypto() + { + JToken token = MakeNestedNullGraph(); + // encryptionSettingForProperty: null is okay because null leaves short-circuit before usage + await EncryptionProcessor.EncryptJTokenAsync(token, encryptionSettingForProperty: null, shouldEscape: false, cancellationToken: CancellationToken.None); + + // Remains structurally the same (all null leaves) + Assert.IsTrue(JToken.DeepEquals(token, MakeNestedNullGraph())); + } + + [TestMethod] + public async Task Traversal_DecryptJTokenAsync_Traverses_Nested_ObjectArray_WithNullLeaves_NoCrypto() + { + JToken token = MakeNestedNullGraph(); + // isEscaped = true to exercise that branch (as if property == "id") + await EncryptionProcessor.DecryptJTokenAsync(token, encryptionSettingForProperty: null, isEscaped: true, cancellationToken: CancellationToken.None); + + Assert.IsTrue(JToken.DeepEquals(token, MakeNestedNullGraph())); + } + + [TestMethod] + public async Task Traversal_EncryptJTokenAsync_ShouldEscapeTrue_SubtreeTraversal_NoCrypto_WithStringIdPresent() + { + // Document has a string id, but we traverse only the 'sub' subtree which has null leaves. + JObject doc = JObject.Parse("{ \"id\": \"abc\", \"sub\": { \"a\": null, \"b\": [ null, { \"c\": null } ] } }"); + + // Take the subtree token to avoid touching the top-level id string. + JToken subtree = doc["sub"]; + await EncryptionProcessor.EncryptJTokenAsync(subtree, encryptionSettingForProperty: null, shouldEscape: true, cancellationToken: CancellationToken.None); + + // The subtree should remain unchanged, and the id property should remain unchanged too. + Assert.AreEqual("abc", doc.Value("id")); + Assert.IsTrue(JToken.DeepEquals(subtree, JToken.Parse("{ \"a\": null, \"b\": [ null, { \"c\": null } ] }"))); + } + + #endregion + + #region Numeric Round-Trip Tests + + [TestMethod] + public async Task Primitives_RoundTrip_Long_Min_Max_Negative_Zero() + { + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algorithm = CreateDeterministicAlgorithm(); + EncryptionSettings settings = CreateSettingsWithInjected("n", algorithm); + + long[] values = new long[] { long.MinValue, -1L, 0L, 1L, long.MaxValue }; + + foreach (long v in values) + { + JObject doc = new JObject { ["id"] = "1", ["n"] = v }; + using System.IO.Stream enc = await EncryptionProcessor.EncryptAsync(EncryptionProcessor.BaseSerializer.ToStream(doc), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + using System.IO.Stream dec = await EncryptionProcessor.DecryptAsync(enc, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject round = EncryptionProcessor.BaseSerializer.FromStream(dec); + Assert.AreEqual(v, round.Value("n")); + } + } + + [TestMethod] + public async Task Primitives_RoundTrip_Double_Extremes_Or_Disallow_NaN_Infinity() + { + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algorithm = CreateDeterministicAlgorithm(); + EncryptionSettings settings = CreateSettingsWithInjected("d", algorithm); + + double[] values = new double[] { double.MinValue, -1.23e308, -1.0, 0.0, 1.0, 1.79e308, double.MaxValue }; + + foreach (double v in values) + { + JObject doc = new JObject { ["id"] = "1", ["d"] = v }; + using System.IO.Stream enc = await EncryptionProcessor.EncryptAsync(EncryptionProcessor.BaseSerializer.ToStream(doc), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + using System.IO.Stream dec = await EncryptionProcessor.DecryptAsync(enc, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject round = EncryptionProcessor.BaseSerializer.FromStream(dec); + Assert.AreEqual(v, round.Value("d"), 0.0); + } + + // Disallow NaN / Infinity + Exception ex; + ex = null; + try { EncryptionProcessor.Serialize(new JValue(double.NaN)); } + catch (Exception e) { ex = e; } + Assert.IsNotNull(ex); + + ex = null; + try { EncryptionProcessor.Serialize(new JValue(double.PositiveInfinity)); } + catch (Exception e) { ex = e; } + Assert.IsNotNull(ex); + + ex = null; + try { EncryptionProcessor.Serialize(new JValue(double.NegativeInfinity)); } + catch (Exception e) { ex = e; } + Assert.IsNotNull(ex); + } + + #endregion + + #region Diagnostics Tests + + [TestMethod] + public async Task Diagnostics_EncryptDecrypt_MultipleProperties_Increments_Counts() + { + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algorithm = CreateDeterministicAlgorithm(); + EncryptionSettings settings = new EncryptionSettings("rid", new List { "/id" }); + EncryptionContainer container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + settings.SetEncryptionSettingForProperty("A", new EncryptionSettingForProperty("cekA", Mde.EncryptionType.Deterministic, container, "dbRid", algorithm)); + settings.SetEncryptionSettingForProperty("B", new EncryptionSettingForProperty("cekB", Mde.EncryptionType.Deterministic, container, "dbRid", algorithm)); + settings.SetEncryptionSettingForProperty("C", new EncryptionSettingForProperty("cekC", Mde.EncryptionType.Deterministic, container, "dbRid", algorithm)); + + JObject doc = new JObject { ["id"] = "1", ["A"] = 1, ["B"] = "x", ["C"] = true }; + + EncryptionDiagnosticsContext diagEnc = new EncryptionDiagnosticsContext(); + using System.IO.Stream enc = await EncryptionProcessor.EncryptAsync(EncryptionProcessor.BaseSerializer.ToStream(doc), settings, diagEnc, CancellationToken.None); + Assert.AreEqual(3, diagEnc.EncryptContent[Constants.DiagnosticsPropertiesEncryptedCount].Value()); + + EncryptionDiagnosticsContext diagDec = new EncryptionDiagnosticsContext(); + using System.IO.Stream dec = await EncryptionProcessor.DecryptAsync(enc, settings, diagDec, CancellationToken.None); + Assert.AreEqual(3, diagDec.DecryptContent[Constants.DiagnosticsPropertiesDecryptedCount].Value()); + + JObject round = EncryptionProcessor.BaseSerializer.FromStream(dec); + Assert.IsTrue(JToken.DeepEquals(doc, round)); + } + + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.Cryptography.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.Cryptography.cs new file mode 100644 index 0000000000..0d11590583 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.Cryptography.cs @@ -0,0 +1,184 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Mde = Microsoft.Data.Encryption.Cryptography; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json.Linq; + using Microsoft.Azure.Cosmos.Encryption.Tests.TestHelpers; + + /// + /// Algorithm and cryptography tests for EncryptionProcessor including randomized algorithms, + /// different encryption modes, and cryptographic behavior verification. + /// + public partial class EncryptionProcessorTests + { + #region Randomized Algorithm Tests + + private static Mde.AeadAes256CbcHmac256EncryptionAlgorithm CreateRandomizedAlgorithm() => TestCryptoHelpers.CreateAlgorithm(Mde.EncryptionType.Randomized); + + private static EncryptionSettings CreateRandomizedSettings(string propertyName, Mde.AeadAes256CbcHmac256EncryptionAlgorithm algo) + { + var settings = new EncryptionSettings("rid", new List { "/id" }); + var container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + settings.SetEncryptionSettingForProperty(propertyName, new EncryptionSettingForProperty("cek1", Mde.EncryptionType.Randomized, container, "dbRid", algo)); + return settings; + } + + [TestMethod] + public async Task Cryptography_Randomized_Encrypt_Twice_DifferentCipher_SameDecrypt() + { + var algo = CreateRandomizedAlgorithm(); + var settings = CreateRandomizedSettings("Secret", algo); + + JObject doc = new JObject { ["id"] = "1", ["Secret"] = new JObject { ["a"] = 1, ["b"] = "x" } }; + + using System.IO.Stream s1 = EncryptionProcessor.BaseSerializer.ToStream(doc); + using System.IO.Stream e1 = await EncryptionProcessor.EncryptAsync(s1, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject enc1 = EncryptionProcessor.BaseSerializer.FromStream(e1); + + using System.IO.Stream s2 = EncryptionProcessor.BaseSerializer.ToStream(doc); + using System.IO.Stream e2 = await EncryptionProcessor.EncryptAsync(s2, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject enc2 = EncryptionProcessor.BaseSerializer.FromStream(e2); + + // Ciphertexts under randomized encryption should differ + Assert.IsFalse(JToken.DeepEquals(enc1["Secret"], enc2["Secret"])); + + // Both decrypt back to the original + using System.IO.Stream d1 = await EncryptionProcessor.DecryptAsync(EncryptionProcessor.BaseSerializer.ToStream(enc1), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + using System.IO.Stream d2 = await EncryptionProcessor.DecryptAsync(EncryptionProcessor.BaseSerializer.ToStream(enc2), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject r1 = EncryptionProcessor.BaseSerializer.FromStream(d1); + JObject r2 = EncryptionProcessor.BaseSerializer.FromStream(d2); + Assert.IsTrue(JToken.DeepEquals(doc, r1)); + Assert.IsTrue(JToken.DeepEquals(doc, r2)); + } + + #endregion + + #region Deterministic vs Randomized Comparison Tests + + [TestMethod] + public async Task Cryptography_Deterministic_Encrypt_Twice_SameCipher() + { + var detAlgo = CreateDeterministicAlgorithm(); + var settings = CreateSettingsWithInjected("Secret", detAlgo); + + JObject doc = new JObject { ["id"] = "1", ["Secret"] = "consistent value" }; + + using System.IO.Stream s1 = EncryptionProcessor.BaseSerializer.ToStream(doc); + using System.IO.Stream e1 = await EncryptionProcessor.EncryptAsync(s1, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject enc1 = EncryptionProcessor.BaseSerializer.FromStream(e1); + + using System.IO.Stream s2 = EncryptionProcessor.BaseSerializer.ToStream(doc); + using System.IO.Stream e2 = await EncryptionProcessor.EncryptAsync(s2, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject enc2 = EncryptionProcessor.BaseSerializer.FromStream(e2); + + // Ciphertexts under deterministic encryption should be identical + Assert.IsTrue(JToken.DeepEquals(enc1["Secret"], enc2["Secret"])); + + // Both decrypt back to the original + using System.IO.Stream d1 = await EncryptionProcessor.DecryptAsync(EncryptionProcessor.BaseSerializer.ToStream(enc1), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + using System.IO.Stream d2 = await EncryptionProcessor.DecryptAsync(EncryptionProcessor.BaseSerializer.ToStream(enc2), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject r1 = EncryptionProcessor.BaseSerializer.FromStream(d1); + JObject r2 = EncryptionProcessor.BaseSerializer.FromStream(d2); + Assert.IsTrue(JToken.DeepEquals(doc, r1)); + Assert.IsTrue(JToken.DeepEquals(doc, r2)); + } + + #endregion + + #region Different Encryption Modes Tests + + [TestMethod] + public async Task Cryptography_MixedEncryptionTypes_SingleDocument() + { + var detAlgo = CreateDeterministicAlgorithm(); + var randAlgo = CreateRandomizedAlgorithm(); + + var settings = new EncryptionSettings("rid", new List { "/id" }); + var container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + + // Configure one property as deterministic, another as randomized + settings.SetEncryptionSettingForProperty("DeterministicProp", new EncryptionSettingForProperty("cek1", Mde.EncryptionType.Deterministic, container, "dbRid", detAlgo)); + settings.SetEncryptionSettingForProperty("RandomizedProp", new EncryptionSettingForProperty("cek2", Mde.EncryptionType.Randomized, container, "dbRid", randAlgo)); + + JObject doc = new JObject + { + ["id"] = "1", + ["DeterministicProp"] = "searchable value", + ["RandomizedProp"] = "secure value", + ["PlainProp"] = "unencrypted value" + }; + + using System.IO.Stream encrypted = await EncryptionProcessor.EncryptAsync(EncryptionProcessor.BaseSerializer.ToStream(doc), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + using System.IO.Stream decrypted = await EncryptionProcessor.DecryptAsync(encrypted, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + + JObject result = EncryptionProcessor.BaseSerializer.FromStream(decrypted); + Assert.IsTrue(JToken.DeepEquals(doc, result)); + } + + #endregion + + #region Key Management Tests + + [TestMethod] + public async Task Cryptography_DifferentKeys_SameAlgorithm() + { + var algo1 = CreateDeterministicAlgorithm(); + var algo2 = CreateDeterministicAlgorithm(); // Different key internally + + var settings1 = CreateSettingsWithInjected("Secret", algo1); + var settings2 = CreateSettingsWithInjected("Secret", algo2); + + JObject doc = new JObject { ["id"] = "1", ["Secret"] = "shared value" }; + + // Encrypt with first key + using System.IO.Stream s1 = EncryptionProcessor.BaseSerializer.ToStream(doc); + using System.IO.Stream e1 = await EncryptionProcessor.EncryptAsync(s1, settings1, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject enc1 = EncryptionProcessor.BaseSerializer.FromStream(e1); + + // Encrypt with second key + using System.IO.Stream s2 = EncryptionProcessor.BaseSerializer.ToStream(doc); + using System.IO.Stream e2 = await EncryptionProcessor.EncryptAsync(s2, settings2, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject enc2 = EncryptionProcessor.BaseSerializer.FromStream(e2); + + // Different keys should produce different ciphertexts (even with deterministic encryption) + Assert.IsFalse(JToken.DeepEquals(enc1["Secret"], enc2["Secret"])); + + // Each decrypts correctly with its own key + using System.IO.Stream d1 = await EncryptionProcessor.DecryptAsync(EncryptionProcessor.BaseSerializer.ToStream(enc1), settings1, operationDiagnostics: null, cancellationToken: CancellationToken.None); + using System.IO.Stream d2 = await EncryptionProcessor.DecryptAsync(EncryptionProcessor.BaseSerializer.ToStream(enc2), settings2, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject r1 = EncryptionProcessor.BaseSerializer.FromStream(d1); + JObject r2 = EncryptionProcessor.BaseSerializer.FromStream(d2); + Assert.IsTrue(JToken.DeepEquals(doc, r1)); + Assert.IsTrue(JToken.DeepEquals(doc, r2)); + } + + #endregion + + #region Algorithm Behavior Tests + + [TestMethod] + public void Cryptography_AlgorithmProperties_ValidConfiguration() + { + var detAlgo = CreateDeterministicAlgorithm(); + var randAlgo = CreateRandomizedAlgorithm(); + + Assert.IsNotNull(detAlgo); + Assert.IsNotNull(randAlgo); + + // These algorithms should be configured correctly for their respective encryption types + // Additional property checks would require access to internal algorithm state + } + + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.DataFormatEncoding.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.DataFormatEncoding.cs new file mode 100644 index 0000000000..a03d244ff3 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.DataFormatEncoding.cs @@ -0,0 +1,391 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Mde = Microsoft.Data.Encryption.Cryptography; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json.Linq; + using Microsoft.Azure.Cosmos.Encryption.Tests.TestHelpers; + + /// + /// Data format and encoding tests for EncryptionProcessor including ID escaping, + /// Unicode handling, feed responses, feed shapes, and value stream encryption. + /// + public partial class EncryptionProcessorTests + { + #region ID Escaping Tests + + [TestMethod] + public void DataFormat_Base64_UriSafe_Roundtrip_With_Url_Problematic_Chars() + { + // bytes that produce '+' and '/' in standard Base64 + // Build input with a wide byte distribution + byte[] input = Enumerable.Range(0, 256).Select(i => (byte)i).ToArray(); + + string uriSafe = EncryptionProcessor.ConvertToBase64UriSafeString(input); + + // Assert it contains neither '+' nor '/' + Assert.IsFalse(uriSafe.Contains('+')); + Assert.IsFalse(uriSafe.Contains('/')); + + byte[] roundtrip = EncryptionProcessor.ConvertFromBase64UriSafeString(uriSafe); + CollectionAssert.AreEqual(input, roundtrip, "URI-safe Base64 conversion should be lossless."); + } + + [TestMethod] + public void DataFormat_Base64_UriSafe_Does_Not_Pad_With_Whitespace() + { + byte[] input = Encoding.UTF8.GetBytes("some id with / and + and ? #"); + string uriSafe = EncryptionProcessor.ConvertToBase64UriSafeString(input); + + // Sanity: No whitespace + Assert.IsFalse(uriSafe.Any(char.IsWhiteSpace)); + + // Roundtrip + byte[] roundtrip = EncryptionProcessor.ConvertFromBase64UriSafeString(uriSafe); + CollectionAssert.AreEqual(input, roundtrip); + } + + #endregion + + #region Unicode Handling Tests + + [TestMethod] + public async Task DataFormat_EncryptDecrypt_Id_With_Unicode_And_ProblemChars_RoundTrips() + { + string id = "id/漢字+emoji😀?hash#back\\slash"; + JObject doc = new JObject { ["id"] = id, ["p"] = 1 }; + EncryptionSettings settings = CreateSettings("id", Algo()); + + using System.IO.Stream enc = await EncryptionProcessor.EncryptAsync(EncryptionProcessor.BaseSerializer.ToStream(doc), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject encDoc = EncryptionProcessor.BaseSerializer.FromStream(enc); + string encId = encDoc.Value("id"); + Assert.IsFalse(encId.Contains('/')); + Assert.IsFalse(encId.Contains('+')); + Assert.IsFalse(encId.Contains('?')); + Assert.IsFalse(encId.Contains('#')); + Assert.IsFalse(encId.Contains('\\')); + + using System.IO.Stream dec = await EncryptionProcessor.DecryptAsync(EncryptionProcessor.BaseSerializer.ToStream(encDoc), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject round = EncryptionProcessor.BaseSerializer.FromStream(dec); + Assert.AreEqual(id, round.Value("id")); + } + + [TestMethod] + public async Task DataFormat_EncryptDecrypt_Id_Large_MixedUnicode_RoundTrips() + { + // Build a large, mixed-unicode id string + StringBuilder sb = new StringBuilder(); + string chunk = "😀漢字🌍🔥/+#?\\"; + for (int i = 0; i < 500; i++) sb.Append(chunk); // length ~3500 chars + string id = sb.ToString(); + + JObject doc = new JObject { ["id"] = id, ["p"] = 1 }; + EncryptionSettings settings = CreateSettings("id", Algo()); + + using System.IO.Stream enc = await EncryptionProcessor.EncryptAsync(EncryptionProcessor.BaseSerializer.ToStream(doc), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject encDoc = EncryptionProcessor.BaseSerializer.FromStream(enc); + string encId = encDoc.Value("id"); + Assert.IsFalse(encId.Contains('/')); + Assert.IsFalse(encId.Contains('+')); + Assert.IsFalse(encId.Contains('?')); + Assert.IsFalse(encId.Contains('#')); + Assert.IsFalse(encId.Contains('\\')); + + using System.IO.Stream dec = await EncryptionProcessor.DecryptAsync(EncryptionProcessor.BaseSerializer.ToStream(encDoc), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject round = EncryptionProcessor.BaseSerializer.FromStream(dec); + Assert.AreEqual(id, round.Value("id")); + } + + #endregion + + #region Feed Response Tests + + [TestMethod] + public async Task DataFormat_DeserializeAndDecryptResponseAsync_Throws_When_Documents_NotArrayOrMissing() + { + // Arrange: Ensure PropertiesToEncrypt.Any() == true so we don't early-return + EncryptionSettings settings = new EncryptionSettings("rid", new List { "/id" }); + EncryptionContainer container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + settings.SetEncryptionSettingForProperty( + "Sensitive", + new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Mde.EncryptionType.Randomized, + encryptionContainer: container, + databaseRid: "dbRid")); + + // Case 1: Missing Documents property + using (System.IO.MemoryStream s1 = ToStream("{ \"_count\": 0 }")) + { + await Assert.ThrowsExceptionAsync( + () => EncryptionProcessor.DeserializeAndDecryptResponseAsync(s1, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None)); + } + + // Case 2: Documents is not an array (object) + using (System.IO.MemoryStream s2 = ToStream("{ \"_count\": 1, \"Documents\": { \"id\": \"1\" } }")) + { + await Assert.ThrowsExceptionAsync( + () => EncryptionProcessor.DeserializeAndDecryptResponseAsync(s2, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None)); + } + + // Case 3: Documents is not an array (string) + using (System.IO.MemoryStream s3 = ToStream("{ \"_count\": 1, \"Documents\": \"oops\" }")) + { + await Assert.ThrowsExceptionAsync( + () => EncryptionProcessor.DeserializeAndDecryptResponseAsync(s3, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None)); + } + } + + [TestMethod] + public async Task DataFormat_DeserializeAndDecryptResponseAsync_EmptyDocumentsArray_NoOp() + { + string responseJson = "{ \"_count\": 0, \"Documents\": [] }"; + using (System.IO.MemoryStream stream = ToStream(responseJson)) + { + EncryptionSettings settings = CreateSettingsWithNoProperties(); + + // With no properties to encrypt, method should return input as-is + using System.IO.Stream result = await EncryptionProcessor.DeserializeAndDecryptResponseAsync(stream, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + + // Expect same content structure + JObject original = JObject.Parse(responseJson); + JObject roundtrip = EncryptionProcessor.BaseSerializer.FromStream(result); + Assert.IsTrue(JToken.DeepEquals(original, roundtrip)); + } + } + + [TestMethod] + public async Task DataFormat_DeserializeAndDecryptResponseAsync_EmptyDocumentsArray_WithConfiguredProps_NoOpButParses() + { + string responseJson = "{ \"_count\": 0, \"Documents\": [] }"; + using (System.IO.MemoryStream stream = ToStream(responseJson)) + { + // Create settings with a mapping; no documents -> no-op + EncryptionSettings settings = new EncryptionSettings("rid", new List { "/id" }); + EncryptionContainer container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + settings.SetEncryptionSettingForProperty( + "sensitive", + new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Mde.EncryptionType.Randomized, + encryptionContainer: container, + databaseRid: "dbRid")); + + using System.IO.Stream result = await EncryptionProcessor.DeserializeAndDecryptResponseAsync(stream, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + + JObject original = JObject.Parse(responseJson); + JObject roundtrip = EncryptionProcessor.BaseSerializer.FromStream(result); + Assert.IsTrue(JToken.DeepEquals(original, roundtrip)); + } + } + + [TestMethod] + public async Task DataFormat_DeserializeAndDecryptResponseAsync_MixedDocuments_AggregatesDiagnosticsOnlyForObjects() + { + // Documents array with: object (has Sensitive: null), number, string, object (no Sensitive) + string responseJson = "{\n \"_count\": 4,\n \"Documents\": [\n { \"id\": \"1\", \"Sensitive\": null },\n 42,\n \"hello\",\n { \"id\": \"2\", \"Other\": true }\n ]\n}"; + + using (System.IO.MemoryStream stream = ToStream(responseJson)) + { + // Build EncryptionSettings with a mapping for Sensitive; null value avoids crypto path. + EncryptionSettings settings = new EncryptionSettings("rid", new List { "/id" }); + EncryptionContainer container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + EncryptionSettingForProperty forProperty = new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Mde.EncryptionType.Randomized, + encryptionContainer: container, + databaseRid: "dbRid"); + settings.SetEncryptionSettingForProperty("Sensitive", forProperty); + + EncryptionDiagnosticsContext diag = new EncryptionDiagnosticsContext(); + + using System.IO.Stream result = await EncryptionProcessor.DeserializeAndDecryptResponseAsync(stream, settings, diag, CancellationToken.None); + + // Only the first object contains the configured property; expect count == 1 + Assert.IsNotNull(diag.DecryptContent); + Assert.AreEqual(1, diag.DecryptContent[Constants.DiagnosticsPropertiesDecryptedCount].Value()); + + // Shape should remain intact. + JObject roundtrip = EncryptionProcessor.BaseSerializer.FromStream(result); + Assert.IsTrue(JToken.DeepEquals(JObject.Parse(responseJson), roundtrip)); + } + } + + [TestMethod] + public async Task DataFormat_FeedResponse_MultipleDocs_Aggregates_Count() + { + // Arrange: two documents with the configured property present + string responseJson = "{\n \"_count\": 2,\n \"Documents\": [\n { \"id\": \"1\", \"Sensitive\": null },\n { \"id\": \"2\", \"Sensitive\": null }\n ]\n}"; + + using (System.IO.MemoryStream stream = ToStream(responseJson)) + { + EncryptionSettings settings = new EncryptionSettings("rid", new List { "/id" }); + EncryptionContainer container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + EncryptionSettingForProperty forProperty = new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Mde.EncryptionType.Randomized, + encryptionContainer: container, + databaseRid: "dbRid"); + settings.SetEncryptionSettingForProperty("Sensitive", forProperty); + + EncryptionDiagnosticsContext diag = new EncryptionDiagnosticsContext(); + using System.IO.Stream result = await EncryptionProcessor.DeserializeAndDecryptResponseAsync(stream, settings, diag, CancellationToken.None); + + // Both objects have the property present -> count should be 2 + Assert.IsNotNull(diag.DecryptContent); + Assert.AreEqual(2, diag.DecryptContent[Constants.DiagnosticsPropertiesDecryptedCount].Value()); + + // Shape preserved + JObject roundtrip = EncryptionProcessor.BaseSerializer.FromStream(result); + Assert.IsTrue(JToken.DeepEquals(JObject.Parse(responseJson), roundtrip)); + } + } + + #endregion + + #region Feed Shape Tests + + [TestMethod] + public async Task DataFormat_ProcessFeedResponse_MaintainsStructure() + { + // Test that feed response processing maintains the overall structure + string feedJson = @"{ + ""_rid"": ""abc"", + ""Documents"": [ + {""id"": ""doc1"", ""data"": ""value1""}, + {""id"": ""doc2"", ""data"": ""value2""} + ], + ""_count"": 2 + }"; + + using (System.IO.MemoryStream stream = ToStream(feedJson)) + { + EncryptionSettings settings = CreateSettingsWithNoProperties(); + + using System.IO.Stream result = await EncryptionProcessor.DeserializeAndDecryptResponseAsync(stream, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + + JObject original = JObject.Parse(feedJson); + JObject processed = EncryptionProcessor.BaseSerializer.FromStream(result); + + Assert.IsTrue(JToken.DeepEquals(original, processed)); + Assert.AreEqual(2, processed["_count"].Value()); + Assert.AreEqual("abc", processed["_rid"].Value()); + } + } + + #endregion + + #region Value Stream Encryption Tests + + [TestMethod] + public async Task DataFormat_EncryptValueStream_Scalar_String_ShouldEscapeFalse_RoundTrip() + { + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algo = Algo(); + // Create settings just to reuse its configured property settings + EncryptionSettings settings = CreateSettings("s", algo); + EncryptionSettingForProperty propSetting = settings.GetEncryptionSettingForProperty("s"); + + using (System.IO.MemoryStream valueStream = ToStream("\"hello\"")) + { + using System.IO.Stream enc = await EncryptionProcessor.EncryptValueStreamAsync(valueStream, propSetting, shouldEscape: false, cancellationToken: CancellationToken.None); + JToken encryptedToken = EncryptionProcessor.BaseSerializer.FromStream(enc); + + // Decrypt via wrapper + JObject wrapper = new JObject { ["s"] = encryptedToken }; + await EncryptionProcessor.DecryptJTokenAsync(wrapper["s"], propSetting, isEscaped: false, cancellationToken: CancellationToken.None); + Assert.AreEqual("hello", wrapper.Value("s")); + } + } + + [TestMethod] + public async Task DataFormat_EncryptValueStream_Scalar_String_ShouldEscapeTrue_RoundTrip() + { + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algo = Algo(); + EncryptionSettings settings = CreateSettings("s", algo); + EncryptionSettingForProperty propSetting = settings.GetEncryptionSettingForProperty("s"); + + using (System.IO.MemoryStream valueStream = ToStream("\"id/with+chars?#\"")) // JSON string: id/with+chars?# + { + using System.IO.Stream enc = await EncryptionProcessor.EncryptValueStreamAsync(valueStream, propSetting, shouldEscape: true, cancellationToken: CancellationToken.None); + JToken encryptedToken = EncryptionProcessor.BaseSerializer.FromStream(enc); + + // Encrypted token must be a URI-safe base64 string + string cipher = encryptedToken.Value(); + Assert.IsFalse(cipher.Contains('/')); + Assert.IsFalse(cipher.Contains('+')); + Assert.IsFalse(cipher.Contains('?')); + Assert.IsFalse(cipher.Contains('#')); + Assert.IsFalse(cipher.Contains('\\')); + + // Decrypt via wrapper + JObject wrapper = new JObject { ["s"] = encryptedToken }; + await EncryptionProcessor.DecryptJTokenAsync(wrapper["s"], propSetting, isEscaped: true, cancellationToken: CancellationToken.None); + Assert.AreEqual("id/with+chars?#", wrapper.Value("s")); + } + } + + [TestMethod] + public async Task DataFormat_EncryptValueStream_Object_Traverse_Encrypts_Leaves_RoundTrip() + { + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algo = Algo(); + EncryptionSettings settings = CreateSettings("s", algo); + EncryptionSettingForProperty propSetting = settings.GetEncryptionSettingForProperty("s"); + + string payload = "{\"a\":1,\"b\":\"x\",\"c\":null,\"d\":[true,2,\"y\",null]}"; + using (System.IO.Stream enc = await EncryptionProcessor.EncryptValueStreamAsync(ToStream(payload), propSetting, shouldEscape: false, cancellationToken: CancellationToken.None)) + { + JToken encryptedToken = EncryptionProcessor.BaseSerializer.FromStream(enc); + + JObject wrapper = new JObject { ["s"] = encryptedToken }; + await EncryptionProcessor.DecryptJTokenAsync(wrapper["s"], propSetting, isEscaped: false, cancellationToken: CancellationToken.None); + + Assert.IsTrue(JToken.DeepEquals(JObject.Parse(payload), wrapper["s"])); + } + } + + [TestMethod] + public async Task DataFormat_EncryptValueStream_ShouldEscapeTrue_With_NonStringLeaf_Throws() + { + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algo = Algo(); + EncryptionSettings settings = CreateSettings("s", algo); + EncryptionSettingForProperty propSetting = settings.GetEncryptionSettingForProperty("s"); + + // Object contains non-string leaf (1) and shouldEscape=true should fail + using (System.IO.MemoryStream valueStream = ToStream("{\"a\":1,\"b\":\"x\"}")) + { + ArgumentException ex = await Assert.ThrowsExceptionAsync( + () => EncryptionProcessor.EncryptValueStreamAsync(valueStream, propSetting, shouldEscape: true, cancellationToken: CancellationToken.None)); + StringAssert.Contains(ex.Message, "value to escape has to be string type"); + } + } + + [TestMethod] + public async Task DataFormat_EncryptValueStream_NullArgs_Throw() + { + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algo = Algo(); + EncryptionSettings settings = CreateSettings("s", algo); + EncryptionSettingForProperty propSetting = settings.GetEncryptionSettingForProperty("s"); + + await Assert.ThrowsExceptionAsync( + () => EncryptionProcessor.EncryptValueStreamAsync(null, propSetting, shouldEscape: false, cancellationToken: CancellationToken.None)); + + using (System.IO.MemoryStream valueStream = ToStream("\"x\"")) + { + await Assert.ThrowsExceptionAsync( + () => EncryptionProcessor.EncryptValueStreamAsync(valueStream, encryptionSettingForProperty: null, shouldEscape: false, cancellationToken: CancellationToken.None)); + } + } + + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.EdgeCases.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.EdgeCases.cs new file mode 100644 index 0000000000..e4a4ecd77e --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.EdgeCases.cs @@ -0,0 +1,231 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Collections.Generic; + using System.Numerics; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Mde = Microsoft.Data.Encryption.Cryptography; + using Newtonsoft.Json.Linq; + using Microsoft.Azure.Cosmos.Encryption.Tests.TestHelpers; + + /// + /// Edge cases and reliability tests for EncryptionProcessor including depth handling, + /// overflow scenarios, no-op operations, null crypto paths, and diagnostics edge cases. + /// + public partial class EncryptionProcessorTests + { + #region Depth Handling Tests + + private static string DeepJson(int depth) + { + // Build nested {"o": {"o": ... {"v": "x"} }} + JObject cur = new JObject { ["v"] = "x" }; + for (int i = 0; i < depth; i++) + { + cur = new JObject { ["o"] = cur }; + } + return cur.ToString(Newtonsoft.Json.Formatting.None); + } + + [TestMethod] + public async Task EdgeCases_EncryptDecrypt_MaxDepthMinusOne_Succeeds() + { + // Base serializer uses MaxDepth = 64; we generate a depth somewhat below that to avoid parser issues + string json = $"{{\"id\":\"d\",\"Secret\":{DeepJson(30)} }}"; // 30 nested levels + EncryptionSettings settings = CreateSettings("Secret", Algo()); + + using System.IO.Stream enc = await EncryptionProcessor.EncryptAsync(EncryptionProcessor.BaseSerializer.ToStream(JObject.Parse(json)), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + using System.IO.Stream dec = await EncryptionProcessor.DecryptAsync(enc, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject round = EncryptionProcessor.BaseSerializer.FromStream(dec); + Assert.AreEqual("x", round.SelectToken("$.Secret..v").Value()); + } + + [TestMethod] + public async Task EdgeCases_EncryptDecrypt_NearMaxDepth_Succeeds() + { + // Push close to MaxDepth (64). Using ~60 nested objects keeps us under the cap considering root and wrappers. + JObject deep = JObject.Parse(DeepJson(60)); + JObject doc = new JObject { ["id"] = "deep", ["Secret"] = deep }; + EncryptionSettings settings = CreateSettings("Secret", Algo()); + + using System.IO.Stream enc = await EncryptionProcessor.EncryptAsync(EncryptionProcessor.BaseSerializer.ToStream(doc), settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + using System.IO.Stream dec = await EncryptionProcessor.DecryptAsync(enc, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + JObject round = EncryptionProcessor.BaseSerializer.FromStream(dec); + Assert.AreEqual("x", round.SelectToken("$.Secret..v").Value()); + } + + #endregion + + #region Overflow Tests + + [TestMethod] + public void EdgeCases_Serialize_BigInteger_Throws() + { + // Current implementation does not support BigInteger and will attempt to coerce to long. + // Verify this results in an exception (OverflowException in the current path). + BigInteger tooLarge = new BigInteger(long.MaxValue) + 1; + JToken token = new JValue(tooLarge); + + try + { + EncryptionProcessor.Serialize(token); + Assert.Fail("Expected an exception when serializing BigInteger, but none was thrown."); + } + catch (Exception ex) + { + // Be tolerant to implementation detail: either OverflowException (from ToObject) + // or InvalidOperationException if validation changes upstream. + Assert.IsTrue( + ex is OverflowException || ex is InvalidOperationException, + $"Expected OverflowException or InvalidOperationException, but got {ex.GetType()}: {ex.Message}"); + } + } + + #endregion + + #region No-Op Decryption Tests + + private static EncryptionSettings CreateSettingsEmpty() + { + return new EncryptionSettings("rid", new List { "/id" }); + } + + private static EncryptionSettings CreateSettingsWithNullMapping(params string[] properties) + { + // Configure real mappings for properties so PropertiesToEncrypt contains them, + // allowing decrypt traversal without modifying internals. + EncryptionSettings settings = CreateSettingsEmpty(); + EncryptionContainer container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + foreach (string p in properties) + { + EncryptionSettingForProperty forProperty = new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Mde.EncryptionType.Randomized, + encryptionContainer: container, + databaseRid: "dbRid"); + settings.SetEncryptionSettingForProperty(p, forProperty); + } + + return settings; + } + + [TestMethod] + public async Task EdgeCases_Decrypt_JObject_NoPropertiesConfigured_ReturnsSameAndZeroCount() + { + JObject doc = JObject.Parse("{ \"id\": \"1\", \"name\": \"n\" }"); + EncryptionSettings settings = CreateSettingsEmpty(); + + (JObject result, int count) = await EncryptionProcessor.DecryptAsync(doc, settings, CancellationToken.None); + + Assert.AreSame(doc, result); + Assert.AreEqual(0, count); + } + + [TestMethod] + public async Task EdgeCases_Decrypt_JObject_WithPropertiesConfigured_ButNoneCiphertext_ReturnsSameAndZeroCount() + { + // Configure a property for encryption that is NOT present in the document, + // so no ciphertext is encountered and decrypt is a no-op with zero count. + JObject doc = JObject.Parse("{ \"id\": \"plaintext\", \"name\": \"n\" }"); + EncryptionSettings settings = CreateSettingsWithNullMapping("Secret"); // 'Secret' not in doc + + (JObject result, int count) = await EncryptionProcessor.DecryptAsync(doc, settings, CancellationToken.None); + + Assert.AreSame(doc, result); + Assert.AreEqual(0, count); + } + + #endregion + + #region Diagnostics Edge Case Tests + + [TestMethod] + public async Task EdgeCases_Diagnostics_EmptyDocument_NoProperties_ZeroCount() + { + EncryptionSettings settings = CreateSettingsWithNoProperties(); + string json = "{}"; + + using (System.IO.Stream input = ToStream(json)) + { + EncryptionDiagnosticsContext diagEnc = new EncryptionDiagnosticsContext(); + System.IO.Stream encrypted = await EncryptionProcessor.EncryptAsync(input, settings, diagEnc, CancellationToken.None); + + // Should have zero properties encrypted + Assert.AreEqual(0, diagEnc.EncryptContent[Constants.DiagnosticsPropertiesEncryptedCount].Value()); + + // Decrypt should also show zero + EncryptionDiagnosticsContext diagDec = new EncryptionDiagnosticsContext(); + System.IO.Stream decrypted = await EncryptionProcessor.DecryptAsync(encrypted, settings, diagDec, CancellationToken.None); + Assert.AreEqual(0, diagDec.DecryptContent[Constants.DiagnosticsPropertiesDecryptedCount].Value()); + } + } + + [TestMethod] + public async Task EdgeCases_Diagnostics_NullValues_DoNotCount() + { + EncryptionSettings settings = CreateSettingsWithInjected("nullProp", CreateDeterministicAlgorithm()); + + string json = "{\"id\":\"test\",\"nullProp\":null,\"other\":\"value\"}"; + + using (System.IO.Stream input = ToStream(json)) + { + EncryptionDiagnosticsContext diagEnc = new EncryptionDiagnosticsContext(); + System.IO.Stream encrypted = await EncryptionProcessor.EncryptAsync(input, settings, diagEnc, CancellationToken.None); + + // Current implementation increments the count when the property exists, even if value is null. + Assert.AreEqual(1, diagEnc.EncryptContent[Constants.DiagnosticsPropertiesEncryptedCount].Value()); + } + } + + #endregion + + #region Stream Edge Case Tests + + [TestMethod] + public async Task Streams_DecryptAsync_NullInput_ReturnsNull() + { + // Arrange + System.IO.Stream input = null; + EncryptionSettings settings = CreateSettingsWithNoProperties(); + + // Act + System.IO.Stream result = await EncryptionProcessor.DecryptAsync(input, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + + // Assert + Assert.IsNull(result); + } + + [TestMethod] + public void Streams_BaseSerializer_ToStream_IsSeekable() + { + JObject obj = JObject.Parse("{ \"id\": \"1\" }"); + using (System.IO.Stream s = EncryptionProcessor.BaseSerializer.ToStream(obj)) + { + Assert.IsTrue(s.CanSeek, "BaseSerializer.ToStream should return a seekable stream."); + } + } + + [TestMethod] + public async Task Streams_EncryptAsync_ReturnsSeekableStream() + { + string json = "{\"id\":\"1\",\"p\":123}"; + EncryptionSettings settings = CreateSettingsWithNoProperties(); + using (System.IO.Stream input = ToStream(json)) + { + System.IO.Stream encrypted = await EncryptionProcessor.EncryptAsync(input, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + using (encrypted) + { + Assert.IsTrue(encrypted.CanSeek, "EncryptAsync should return a seekable stream to satisfy downstream Debug.Assert invariants."); + } + } + } + + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.Validation.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.Validation.cs new file mode 100644 index 0000000000..43ce7e10ed --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.Validation.cs @@ -0,0 +1,258 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.IO; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json.Linq; + using Mde = Microsoft.Data.Encryption.Cryptography; + + /// + /// Validation tests for EncryptionProcessor including argument validation, + /// settings validation, unsupported types, and type markers. + /// + public partial class EncryptionProcessorTests + { + #region Argument Validation Tests + + [TestMethod] + public async Task Validation_EncryptAsync_NullInput_ThrowsArgumentNullException() + { + await Assert.ThrowsExceptionAsync( + () => EncryptionProcessor.EncryptAsync(input: null, encryptionSettings: CreateSettingsForId(), operationDiagnostics: null, cancellationToken: CancellationToken.None), "input"); + } + + [TestMethod] + public async Task Validation_EncryptAsync_NullSettings_ThrowsOrFailsPredictably() + { + using System.IO.MemoryStream input = ToStream("{\"id\":\"1\"}"); + try + { + await EncryptionProcessor.EncryptAsync(input: input, encryptionSettings: null, operationDiagnostics: null, cancellationToken: CancellationToken.None); + Assert.Fail("Expected an exception when encryptionSettings is null."); + } + catch (NullReferenceException) + { + // Current implementation: NRE when accessing PropertiesToEncrypt; acceptable documented behavior for now. + } + catch (ArgumentNullException) + { + // Future improvement may throw ANE; accept either to avoid test fragility. + } + } + + [TestMethod] + public async Task Validation_EncryptAsync_IdNonStringWithShouldEscape_ThrowsArgumentException() + { + // Arrange: id is an integer, settings configured to encrypt 'id' which triggers shouldEscape + EncryptionSettings settings = CreateSettingsForId(); + using System.IO.MemoryStream input = ToStream("{\"id\": 42, \"p\": 1}"); + + // Act & Assert + ArgumentException ex = await Assert.ThrowsExceptionAsync( + () => EncryptionProcessor.EncryptAsync(input, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None)); + StringAssert.Contains(ex.Message, "value to escape has to be string type"); + } + + #endregion + + #region Settings Validation Tests + + private static EncryptionSettings CreateSettingsWithMissingMapping(params string[] properties) + { + // Create settings and only declare PropertiesToEncrypt via real mappings, then remove them + // to simulate missing mapping when traversing documents. + EncryptionSettings settings = new EncryptionSettings("rid", new System.Collections.Generic.List { "/id" }); + EncryptionContainer container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + foreach (string p in properties) + { + EncryptionSettingForProperty forProperty = new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Mde.EncryptionType.Randomized, + encryptionContainer: container, + databaseRid: "dbRid"); + settings.SetEncryptionSettingForProperty(p, forProperty); + } + + // Now clear the mapping dictionary via reflection to simulate Keys present but value missing. + System.Reflection.FieldInfo dictField = typeof(EncryptionSettings).GetField("encryptionSettingsDictByPropertyName", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!; + System.Collections.Generic.Dictionary dict = (System.Collections.Generic.Dictionary)dictField.GetValue(settings); + foreach (string p in properties) + { + dict[p] = null; + } + + return settings; + } + + [TestMethod] + public async Task Validation_EncryptAsync_PropertyWithoutSetting_Throws_And_DoesNotDisposeInput() + { + // Arrange: The item contains property 'foo', settings list 'foo' for encryption, but no mapping is configured. + using System.IO.MemoryStream input = ToStream("{\"id\":\"1\",\"foo\":123}"); + EncryptionSettings settings = CreateSettingsWithMissingMapping("foo"); + + // Act + try + { + await EncryptionProcessor.EncryptAsync(input, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + Assert.Fail("Expected ArgumentException due to missing EncryptionSettingForProperty mapping."); + } + catch (ArgumentException ex) + { + StringAssert.Contains(ex.Message, "Invalid Encryption Setting for the Property:foo"); + } + + // Assert: The input was fully consumed. Some serializers may dispose the stream during read. + try + { + Assert.AreEqual(input.Length, input.Position, "Input stream position should be at end after failure."); + } + catch (ObjectDisposedException) + { + // Acceptable: FromStream may dispose the input stream after reading. + } + } + + [TestMethod] + public async Task Validation_DecryptAsync_PropertyWithoutSetting_Throws_And_DoesNotDisposeInput() + { + // Arrange: The document contains property 'bar', settings list 'bar' for encryption, but no mapping is configured. + using System.IO.MemoryStream input = ToStream("{\"id\":\"1\",\"bar\":\"someValue\"}"); + EncryptionSettings settings = CreateSettingsWithMissingMapping("bar"); + + // Act + try + { + await EncryptionProcessor.DecryptAsync(input, settings, operationDiagnostics: null, cancellationToken: CancellationToken.None); + Assert.Fail("Expected ArgumentException due to missing EncryptionSettingForProperty mapping."); + } + catch (ArgumentException ex) + { + StringAssert.Contains(ex.Message, "Invalid Encryption Setting for Property:bar"); + } + + // Assert: Input should NOT be disposed, and since it was fully read, position should be at end. + Assert.IsTrue(input.CanRead, "Input stream should not be disposed on failure."); + Assert.AreEqual(input.Length, input.Position, "Input stream position should be at end after failure."); + } + + #endregion + + #region Unsupported Types Tests + + [TestMethod] + public void Validation_Serialize_UnsupportedTypes_ShouldThrow_InvalidOperationException() + { + // Guid + Exception ex = null; + try { _ = EncryptionProcessor.Serialize(new JValue(Guid.NewGuid())); } + catch (Exception e) { ex = e; } + Assert.IsNotNull(ex); + Assert.IsInstanceOfType(ex, typeof(InvalidOperationException)); + + // Bytes + ex = null; + try { _ = EncryptionProcessor.Serialize(new JValue(new byte[] { 1, 2, 3, 4 })); } + catch (Exception e) { ex = e; } + Assert.IsNotNull(ex); + Assert.IsInstanceOfType(ex, typeof(InvalidOperationException)); + + // TimeSpan + ex = null; + try { _ = EncryptionProcessor.Serialize(new JValue(TimeSpan.FromMinutes(5))); } + catch (Exception e) { ex = e; } + Assert.IsNotNull(ex); + Assert.IsInstanceOfType(ex, typeof(InvalidOperationException)); + + // Uri (additional unsupported type) + ex = null; + try { _ = EncryptionProcessor.Serialize(new JValue(new Uri("https://example.com"))); } + catch (Exception e) { ex = e; } + Assert.IsNotNull(ex); + Assert.IsInstanceOfType(ex, typeof(InvalidOperationException)); + } + + [TestMethod] + public void Validation_Serialize_ShouldEscape_NonString_ShouldThrow_ArgumentException() + { + // shouldEscape path is enforced in SerializeAndEncryptValueAsync; use the public EncryptAsync with 'id' configured and non-string id. + EncryptionSettings settings = new EncryptionSettings("rid", new System.Collections.Generic.List { "/id" }); + EncryptionContainer container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + EncryptionSettingForProperty forProperty = new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Mde.EncryptionType.Deterministic, + encryptionContainer: container, + databaseRid: "dbRid"); + settings.SetEncryptionSettingForProperty("id", forProperty); + + using System.IO.MemoryStream s = new System.IO.MemoryStream(Encoding.UTF8.GetBytes("{\"id\":42}")); + ArgumentException ex = Assert.ThrowsExceptionAsync( + () => EncryptionProcessor.EncryptAsync(s, settings, operationDiagnostics: null, cancellationToken: default)).GetAwaiter().GetResult(); + StringAssert.Contains(ex.Message, "value to escape has to be string type"); + } + + #endregion + + #region Type Marker Tests + + + + [TestMethod] + public void TypeMarker_RoundTrips_For_All_Supported_Types() + { + // Boolean + (EncryptionProcessor.TypeMarker mBool, byte[] bBool) = EncryptionProcessor.Serialize(new JValue(true)); + Assert.AreEqual(EncryptionProcessor.TypeMarker.Boolean, mBool); + Assert.AreEqual(true, EncryptionProcessor.DeserializeAndAddProperty(bBool, mBool).Value()); + + // Double + (EncryptionProcessor.TypeMarker mDouble, byte[] bDouble) = EncryptionProcessor.Serialize(new JValue(3.14159)); + Assert.AreEqual(EncryptionProcessor.TypeMarker.Double, mDouble); + Assert.AreEqual(3.14159, EncryptionProcessor.DeserializeAndAddProperty(bDouble, mDouble).Value(), 0.0); + + // Long + (EncryptionProcessor.TypeMarker mLong, byte[] bLong) = EncryptionProcessor.Serialize(new JValue(42L)); + Assert.AreEqual(EncryptionProcessor.TypeMarker.Long, mLong); + Assert.AreEqual(42L, EncryptionProcessor.DeserializeAndAddProperty(bLong, mLong).Value()); + + // String + (EncryptionProcessor.TypeMarker mString, byte[] bString) = EncryptionProcessor.Serialize(new JValue("hello")); + Assert.AreEqual(EncryptionProcessor.TypeMarker.String, mString); + Assert.AreEqual("hello", EncryptionProcessor.DeserializeAndAddProperty(bString, mString).Value()); + } + + [TestMethod] + public async Task TypeMarker_Invalid_Or_Malformed_Cipher_Throws() + { + // Build real ciphertext first + Mde.AeadAes256CbcHmac256EncryptionAlgorithm algorithm = CreateDeterministicAlgorithm(); + EncryptionSettings settings = CreateSettingsWithInjected("p", algorithm); + EncryptionSettingForProperty propSetting = settings.GetEncryptionSettingForProperty("p"); + + // Encrypt a simple string with shouldEscape=false so we get byte[] token + using System.IO.Stream enc = await EncryptionProcessor.EncryptValueStreamAsync(ToStream("\"abc\""), propSetting, shouldEscape: false, cancellationToken: CancellationToken.None); + JToken token = EncryptionProcessor.BaseSerializer.FromStream(enc); + byte[] cipherWithMarker = token.ToObject(); + Assert.IsNotNull(cipherWithMarker); + Assert.IsTrue(cipherWithMarker.Length > 1); + + // Tamper the type marker to an invalid value (e.g., 0 which is not defined) + byte[] tampered = (byte[])cipherWithMarker.Clone(); + tampered[0] = 0; // invalid TypeMarker + + // Decrypt path should throw when DeserializeAndAddProperty sees invalid marker + JObject wrapper = new JObject { ["p"] = tampered }; + await Assert.ThrowsExceptionAsync( + () => EncryptionProcessor.DecryptJTokenAsync(wrapper["p"], propSetting, isEscaped: false, cancellationToken: CancellationToken.None)); + } + + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.cs new file mode 100644 index 0000000000..36d589db06 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/EncryptionProcessorTests.cs @@ -0,0 +1,97 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Runtime.Serialization; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Mde = Microsoft.Data.Encryption.Cryptography; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json.Linq; + using Microsoft.Azure.Cosmos.Encryption.Tests.TestHelpers; + + /// + /// Comprehensive test suite for EncryptionProcessor functionality. + /// This class is split into multiple partial classes organized by test category for better maintainability. + /// + [TestClass] + public partial class EncryptionProcessorTests + { + #region Shared Test Utilities + + // Thin wrappers so existing partial classes can call these helpers. + protected static MemoryStream ToStream(string json) + { + return StreamTestHelpers.ToStream(json); + } + protected static string ReadToEnd(Stream s) + { + return StreamTestHelpers.ReadToEnd(s); + } + + private static EncryptionSettings CreateSettingsWithNoProperties() + { + // Use the internal constructor normally; leaving the mapping empty results in + // PropertiesToEncrypt being an empty enumeration (no encryption work performed). + return new EncryptionSettings("rid", new List { "/id" }); + } + + private static EncryptionSettings CreateSettingsForId() + { + EncryptionSettings settings = new EncryptionSettings("rid", new List { "/id" }); + // Use an uninitialized container; it won't be used in the failure paths these tests exercise. + EncryptionContainer container = (EncryptionContainer)FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + EncryptionSettingForProperty forProperty = new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Mde.EncryptionType.Deterministic, + encryptionContainer: container, + databaseRid: "dbRid"); + settings.SetEncryptionSettingForProperty("id", forProperty); + return settings; + } + + private static Mde.AeadAes256CbcHmac256EncryptionAlgorithm CreateDeterministicAlgorithm() + { + return TestCryptoHelpers.CreateAlgorithm(Mde.EncryptionType.Deterministic); + } + + private static EncryptionSettings CreateSettingsWithInjected(string propertyName, Mde.AeadAes256CbcHmac256EncryptionAlgorithm algorithm) + { + EncryptionSettings settings = new EncryptionSettings("rid", new List { "/id" }); + EncryptionContainer container = (EncryptionContainer)FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + EncryptionSettingForProperty forProperty = new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: Mde.EncryptionType.Deterministic, + encryptionContainer: container, + databaseRid: "dbRid", + injectedAlgorithm: algorithm); + settings.SetEncryptionSettingForProperty(propertyName, forProperty); + return settings; + } + + private static EncryptionSettings CreateSettings(string prop, Mde.AeadAes256CbcHmac256EncryptionAlgorithm algo) + { + EncryptionSettings settings = new EncryptionSettings("rid", new List { "/id" }); + EncryptionContainer container = (EncryptionContainer)FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + settings.SetEncryptionSettingForProperty(prop, new EncryptionSettingForProperty("cek1", Mde.EncryptionType.Deterministic, container, "dbRid", algo)); + return settings; + } + + private static Mde.AeadAes256CbcHmac256EncryptionAlgorithm Algo() + { + return TestCryptoHelpers.CreateAlgorithm(Mde.EncryptionType.Deterministic); + } + + // (Removed local KEK shim; rely on TestHelpers.TestCryptoHelpers instead.) + + #endregion + + // Documentation moved to XML comments and README. Removed no-op test. + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Helpers/UnsafeAccessors.Net8.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Helpers/UnsafeAccessors.Net8.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Microsoft.Azure.Cosmos.Encryption.Tests.csproj b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Microsoft.Azure.Cosmos.Encryption.Tests.csproj index 7f03149a5b..6fad8a84a6 100644 --- a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Microsoft.Azure.Cosmos.Encryption.Tests.csproj +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Microsoft.Azure.Cosmos.Encryption.Tests.csproj @@ -13,6 +13,7 @@ + @@ -21,6 +22,7 @@ + @@ -28,19 +30,11 @@ - + Always - - - - - - - - diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/TestHelpers/StreamTestHelpers.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/TestHelpers/StreamTestHelpers.cs new file mode 100644 index 0000000000..ad6ebf1880 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/TestHelpers/StreamTestHelpers.cs @@ -0,0 +1,79 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests.TestHelpers +{ + using System; + using System.IO; + using System.Text; + + public static class StreamTestHelpers + { + public static MemoryStream ToStream(string json) + { + if (json is null) + { + throw new ArgumentNullException(nameof(json)); + } + + return new MemoryStream(Encoding.UTF8.GetBytes(json), writable: false); + } + + public static string ReadToEnd(Stream s) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + + if (s.CanSeek) + { + s.Position = 0; + using var sr = new StreamReader(s, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true); + return sr.ReadToEnd(); + } + + using var buffer = new MemoryStream(); + s.CopyTo(buffer); + buffer.Position = 0; + using var sr2 = new StreamReader(buffer, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: false); + return sr2.ReadToEnd(); + } + + // Test-only wrapper to verify disposal explicitly + public sealed class TrackingStream : Stream + { + private readonly Stream inner; + + public TrackingStream(Stream inner) + { + this.inner = inner ?? throw new ArgumentNullException(nameof(inner)); + } + + public bool Disposed { get; private set; } + + public override bool CanRead => this.inner.CanRead; + public override bool CanSeek => this.inner.CanSeek; + public override bool CanWrite => this.inner.CanWrite; + public override long Length => this.inner.Length; + public override long Position { get => this.inner.Position; set => this.inner.Position = value; } + + public override void Flush() => this.inner.Flush(); + public override int Read(byte[] buffer, int offset, int count) => this.inner.Read(buffer, offset, count); + public override long Seek(long offset, SeekOrigin origin) => this.inner.Seek(offset, origin); + public override void SetLength(long value) => this.inner.SetLength(value); + public override void Write(byte[] buffer, int offset, int count) => this.inner.Write(buffer, offset, count); + + protected override void Dispose(bool disposing) + { + if (disposing && !this.Disposed) + { + this.Disposed = true; + this.inner.Dispose(); + } + base.Dispose(disposing); + } + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/TestHelpers/TestCryptoHelpers.cs b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/TestHelpers/TestCryptoHelpers.cs new file mode 100644 index 0000000000..3004f5f800 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/TestHelpers/TestCryptoHelpers.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption.Tests.TestHelpers +{ + using System.Collections.Generic; + using Mde = Microsoft.Data.Encryption.Cryptography; + + internal static class TestCryptoHelpers + { + public class DummyKeyEncryptionKey : Mde.KeyEncryptionKey + { + public DummyKeyEncryptionKey() : base(name: "testKek", path: "test://kek", keyStoreProvider: new DummyProvider()) { } + + private class DummyProvider : Mde.EncryptionKeyStoreProvider + { + public override string ProviderName => "testProvider"; + public override byte[] UnwrapKey(string encryptionKeyId, Mde.KeyEncryptionKeyAlgorithm algorithm, byte[] encryptedKey) => encryptedKey; + public override byte[] WrapKey(string encryptionKeyId, Mde.KeyEncryptionKeyAlgorithm algorithm, byte[] key) => key; + public override byte[] Sign(string encryptionKeyId, bool allowEnclaveComputations) => new byte[] { 1, 2, 3 }; + public override bool Verify(string encryptionKeyId, bool allowEnclaveComputations, byte[] signature) => true; + } + } + + public static Mde.AeadAes256CbcHmac256EncryptionAlgorithm CreateAlgorithm(Mde.EncryptionType type) + { + var kek = new DummyKeyEncryptionKey(); + var pdek = new Mde.ProtectedDataEncryptionKey("pdek-" + type.ToString().ToLowerInvariant(), kek); + return new Mde.AeadAes256CbcHmac256EncryptionAlgorithm(pdek, type); + } + + public static EncryptionSettings CreateSettingsWithInjected(string propertyName, Mde.EncryptionType type) + { + var algo = CreateAlgorithm(type); + return CreateSettingsWithInjected(propertyName, type, algo); + } + + public static EncryptionSettings CreateSettingsWithInjected(string propertyName, Mde.EncryptionType type, Mde.AeadAes256CbcHmac256EncryptionAlgorithm algo) + { + var settings = new EncryptionSettings("rid", new List { "/id" }); + var container = (EncryptionContainer)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(EncryptionContainer)); + var forProperty = new EncryptionSettingForProperty( + clientEncryptionKeyId: "cek1", + encryptionType: type, + encryptionContainer: container, + databaseRid: "dbRid", + injectedAlgorithm: algo); + settings.SetEncryptionSettingForProperty(propertyName, forProperty); + return settings; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Samples/Usage/Encryption/Encryption.csproj b/Microsoft.Azure.Cosmos.Samples/Usage/Encryption/Encryption.csproj index a0e8abd977..da5633750a 100644 --- a/Microsoft.Azure.Cosmos.Samples/Usage/Encryption/Encryption.csproj +++ b/Microsoft.Azure.Cosmos.Samples/Usage/Encryption/Encryption.csproj @@ -9,12 +9,12 @@ True - - - + + + - + diff --git a/Microsoft.Azure.Cosmos.Samples/Usage/ReEncryption/ReEncryption.csproj b/Microsoft.Azure.Cosmos.Samples/Usage/ReEncryption/ReEncryption.csproj index 6d72c453ee..a44799a6c4 100644 --- a/Microsoft.Azure.Cosmos.Samples/Usage/ReEncryption/ReEncryption.csproj +++ b/Microsoft.Azure.Cosmos.Samples/Usage/ReEncryption/ReEncryption.csproj @@ -9,13 +9,13 @@ True - - + + - + diff --git a/Microsoft.Azure.Cosmos.Samples/Usage/SystemTextJson/CosmosSystemTextJsonSerializer.cs b/Microsoft.Azure.Cosmos.Samples/Usage/SystemTextJson/CosmosSystemTextJsonSerializer.cs index 5213e3bb7d..3331f5155b 100644 --- a/Microsoft.Azure.Cosmos.Samples/Usage/SystemTextJson/CosmosSystemTextJsonSerializer.cs +++ b/Microsoft.Azure.Cosmos.Samples/Usage/SystemTextJson/CosmosSystemTextJsonSerializer.cs @@ -47,7 +47,7 @@ public override T FromStream(Stream stream) public override Stream ToStream(T input) { MemoryStream streamPayload = new MemoryStream(); - this.systemTextJsonSerializer.Serialize(streamPayload, input, input.GetType(), default); + this.systemTextJsonSerializer.Serialize(streamPayload, input, typeof(T), default); streamPayload.Position = 0; return streamPayload; } diff --git a/Microsoft.Azure.Cosmos.sln b/Microsoft.Azure.Cosmos.sln index 2cef40e601..cec2d9a5f4 100644 --- a/Microsoft.Azure.Cosmos.sln +++ b/Microsoft.Azure.Cosmos.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.14.36310.24 d17.14 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29123.88 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Cosmos", "Microsoft.Azure.Cosmos\src\Microsoft.Azure.Cosmos.csproj", "{36F6F6A8-CEC8-4261-9948-903495BC3C25}" EndProject @@ -22,6 +22,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{804C595B-D0DA-48F1-A40D-97BB014736CB}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + .globalconfig = .globalconfig EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Encryption.Custom", "Encryption.Custom", "{51F858D8-707E-4F21-BCC6-4D6123832E4F}" @@ -180,18 +181,6 @@ Global {021DDC27-02EF-42C4-9A9E-AA600833C2EE}.Release|Any CPU.Build.0 = Release|Any CPU {021DDC27-02EF-42C4-9A9E-AA600833C2EE}.Release|x64.ActiveCfg = Release|Any CPU {021DDC27-02EF-42C4-9A9E-AA600833C2EE}.Release|x64.Build.0 = Release|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Cover|Any CPU.ActiveCfg = Debug|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Cover|Any CPU.Build.0 = Debug|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Cover|x64.ActiveCfg = Debug|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Cover|x64.Build.0 = Debug|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Debug|x64.ActiveCfg = Debug|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Debug|x64.Build.0 = Debug|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Release|Any CPU.Build.0 = Release|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Release|x64.ActiveCfg = Release|Any CPU - {D744906A-1091-403F-B0B6-794DE045169A}.Release|x64.Build.0 = Release|Any CPU {CE4D6DA8-148D-4A98-943B-D8C2D532E1DC}.Cover|Any CPU.ActiveCfg = Debug|Any CPU {CE4D6DA8-148D-4A98-943B-D8C2D532E1DC}.Cover|Any CPU.Build.0 = Debug|Any CPU {CE4D6DA8-148D-4A98-943B-D8C2D532E1DC}.Cover|x64.ActiveCfg = Debug|Any CPU diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/AssemblyInfo.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/AssemblyInfo.cs index 7b4177e175..d44b531839 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/AssemblyInfo.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/AssemblyInfo.cs @@ -3,8 +3,7 @@ //------------------------------------------------------------ using System.Runtime.CompilerServices; -using Microsoft.Azure.Cosmos.Direct; [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyKeys.MoqPublicKey)] [assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.FaultInjection.Tests" + AssemblyKeys.ProductPublicKey)] -[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.FaultInjection.Tests" + AssemblyKeys.TestPublicKey)] \ No newline at end of file +[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.FaultInjection.Tests" + AssemblyKeys.TestPublicKey)] diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjection.csproj b/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjection.csproj index f2589db30a..7fb921d3dd 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjection.csproj +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjection.csproj @@ -53,6 +53,7 @@ + diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjectionRuleBuilder.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjectionRuleBuilder.cs index dd512366a1..0a1848ea6f 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjectionRuleBuilder.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjectionRuleBuilder.cs @@ -153,7 +153,9 @@ private void ValidateGatewayConnection() && serverErrorResult?.GetServerErrorType() != FaultInjectionServerErrorType.DatabaseAccountNotFound && serverErrorResult?.GetServerErrorType() != FaultInjectionServerErrorType.ServiceUnavailable && serverErrorResult?.GetServerErrorType() != FaultInjectionServerErrorType.InternalServerError - && serverErrorResult?.GetServerErrorType() != FaultInjectionServerErrorType.LeaseNotFound) + && serverErrorResult?.GetServerErrorType() != FaultInjectionServerErrorType.LeaseNotFound + && serverErrorResult?.GetServerErrorType() != FaultInjectionServerErrorType.Unauthorized + && serverErrorResult?.GetServerErrorType() != FaultInjectionServerErrorType.AadTokenRevoked) { throw new ArgumentException($"{serverErrorResult?.GetServerErrorType()} is not supported for metadata requests."); } diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjectionServerErrorType.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjectionServerErrorType.cs index 12045d1155..afb6d019c6 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjectionServerErrorType.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/FaultInjectionServerErrorType.cs @@ -80,5 +80,15 @@ public enum FaultInjectionServerErrorType /// 410:1022 Lease not Found /// LeaseNotFound, + + /// + /// 401: Unauthorized + /// + Unauthorized, + + /// + /// 401:5013 AAD token revoked + /// + AadTokenRevoked } } diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionRuleProcessor.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionRuleProcessor.cs index 6fa1c6e56d..37aa3e1a73 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionRuleProcessor.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionRuleProcessor.cs @@ -189,7 +189,8 @@ await BackoffRetryUtility>.ExecuteAsync( result.GetDelay(), result.GetSuppressServiceRequests(), result.GetInjectionRate(), - this.applicationContext)); + this.applicationContext, + this.globalEndpointManager)); } private async Task GetEffectiveConnectionErrorRule(FaultInjectionRule rule) diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs index 6cac1ccbd1..35a7cdb654 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs @@ -8,6 +8,7 @@ namespace Microsoft.Azure.Cosmos.FaultInjection using System.Net; using System.Net.Http.Headers; using System.Text; + using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; using Microsoft.Azure.Documents.Rntbd; @@ -23,6 +24,7 @@ internal class FaultInjectionServerErrorResultInternal private readonly bool suppressServiceRequest; private readonly double injectionRate; private readonly FaultInjectionApplicationContext applicationContext; + private readonly GlobalEndpointManager globalEndpointManager; /// /// Constructor for FaultInjectionServerErrorResultInternal @@ -32,13 +34,15 @@ internal class FaultInjectionServerErrorResultInternal /// /// /// + /// public FaultInjectionServerErrorResultInternal( - FaultInjectionServerErrorType serverErrorType, - int times, - TimeSpan delay, + FaultInjectionServerErrorType serverErrorType, + int times, + TimeSpan delay, bool suppressServiceRequest, double injectionRate, - FaultInjectionApplicationContext applicationContext) + FaultInjectionApplicationContext applicationContext, + GlobalEndpointManager globalEndpointManager) { this.serverErrorType = serverErrorType; this.times = times; @@ -46,6 +50,7 @@ public FaultInjectionServerErrorResultInternal( this.suppressServiceRequest = suppressServiceRequest; this.injectionRate = injectionRate; this.applicationContext = applicationContext; + this.globalEndpointManager = globalEndpointManager; } /// @@ -164,7 +169,7 @@ public StoreResponse GetInjectedServerError(ChannelCallArguments args, string ru Headers = retryWithHeaders, ResponseBody = new MemoryStream(FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Retry With, rule: {ruleId}")) }; - + return storeResponse; case FaultInjectionServerErrorType.TooManyRequests: @@ -205,7 +210,7 @@ public StoreResponse GetInjectedServerError(ChannelCallArguments args, string ru Headers = internalServerErrorHeaders, ResponseBody = new MemoryStream(FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Internal Server Error, rule: {ruleId}")) }; - + return storeResponse; case FaultInjectionServerErrorType.ReadSessionNotAvailable: @@ -223,7 +228,7 @@ public StoreResponse GetInjectedServerError(ChannelCallArguments args, string ru Headers = readSessionHeaders, ResponseBody = new MemoryStream(FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Read Session Not Available, rule: {ruleId}")) }; - + return storeResponse; case FaultInjectionServerErrorType.PartitionIsMigrating: @@ -237,7 +242,7 @@ public StoreResponse GetInjectedServerError(ChannelCallArguments args, string ru Headers = partitionMigrationHeaders, ResponseBody = new MemoryStream(FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Partition Migrating, rule: {ruleId}")) }; - + return storeResponse; case FaultInjectionServerErrorType.PartitionIsSplitting: @@ -265,7 +270,27 @@ public StoreResponse GetInjectedServerError(ChannelCallArguments args, string ru }; return storeResponse; - + case FaultInjectionServerErrorType.Unauthorized: + INameValueCollection unauthorizedHeaders = args.RequestHeaders; + unauthorizedHeaders.Set(WFConstants.BackendHeaders.LocalLSN, lsn); + storeResponse = new StoreResponse() + { + Status = 401, + Headers = unauthorizedHeaders, + ResponseBody = new MemoryStream(FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Unauthorized, rule: {ruleId}")) + }; + return storeResponse; + case FaultInjectionServerErrorType.AadTokenRevoked: + INameValueCollection aadTokenRevokedHeaders = args.RequestHeaders; + aadTokenRevokedHeaders.Set(WFConstants.BackendHeaders.LocalLSN, lsn); + aadTokenRevokedHeaders.Set(WFConstants.BackendHeaders.SubStatus, "5013"); + storeResponse = new StoreResponse() + { + Status = 401, + Headers = aadTokenRevokedHeaders, + ResponseBody = new MemoryStream(FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Aad Token Revoked, rule: {ruleId}")) + }; + return storeResponse; default: throw new ArgumentException($"Server error type {this.serverErrorType} is not supported"); } @@ -284,23 +309,27 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st //Global or Local lsn? string lsn = dsr.RequestContext.QuorumSelectedLSN.ToString(CultureInfo.InvariantCulture); INameValueCollection headers = dsr.Headers; + bool isProxyCall = this.IsProxyCall(dsr); switch (this.serverErrorType) { case FaultInjectionServerErrorType.Gone: - + httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Gone, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Gone, (int)SubStatusCodes.ServerGenerated410, "Gone", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Gone, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -309,43 +338,48 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.TooManyRequests: - + httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.TooManyRequests, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: TooManyRequests, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.TooManyRequests, (int)SubStatusCodes.RUBudgetExceeded, "TooManyRequests", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: TooManyRequests, rule: {ruleId}"))), }; - - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromMilliseconds(500)); httpResponse.Headers.Add( - WFConstants.BackendHeaders.SubStatus, + WFConstants.BackendHeaders.SubStatus, ((int)SubStatusCodes.RUBudgetExceeded).ToString(CultureInfo.InvariantCulture)); httpResponse.Headers.Add(WFConstants.BackendHeaders.LocalLSN, lsn); return httpResponse; case FaultInjectionServerErrorType.Timeout: - + httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.RequestTimeout, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Timeout, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.RequestTimeout, (int)SubStatusCodes.Unknown, "Timeout", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Timeout, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -355,19 +389,22 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.InternalServerError: - + httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.InternalServerError, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Internal Server Error, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.InternalServerError, (int)SubStatusCodes.Unknown, "InternalServerError", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: InternalServerError, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -377,20 +414,23 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.ReadSessionNotAvailable: - + const string badSesstionToken = "1:1#1#1=1#1=1"; httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.NotFound, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Read Session Not Available, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.NotFound, (int)SubStatusCodes.ReadSessionNotAvailable, "ReadSessionNotAvailable", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: ReadSessionNotAvailable, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -401,19 +441,22 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.PartitionIsMigrating: - + httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: PartitionIsMigrating, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Gone, (int)SubStatusCodes.CompletingPartitionMigration, "PartitionIsMigrating", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: PartitionIsMigrating, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -423,19 +466,22 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.PartitionIsSplitting: - + httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: PartitionIsSplitting, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Gone, (int)SubStatusCodes.CompletingSplit, "PartitionIsSplitting", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: PartitionIsSplitting, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -448,16 +494,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.ServiceUnavailable, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Service Unavailable, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.ServiceUnavailable, (int)SubStatusCodes.Unknown, "ServiceUnavailable", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: ServiceUnavailable, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -467,19 +516,22 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.DatabaseAccountNotFound: - + httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.Forbidden, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: DatabaseAccountNotFound, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Forbidden, (int)SubStatusCodes.DatabaseAccountNotFound, "DatabaseAccountNotFound", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: DatabaseAccountNotFound, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -492,16 +544,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: LeaseNotFound, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Gone, (int)SubStatusCodes.LeaseNotFound, "LeaseNotFound", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: LeaseNotFound, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -509,12 +564,83 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse.Headers.Add(WFConstants.BackendHeaders.LocalLSN, lsn); return httpResponse; - + case FaultInjectionServerErrorType.Unauthorized: + httpResponse = new HttpResponseMessage + { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), + StatusCode = HttpStatusCode.Unauthorized, + Content = new FauntInjectionHttpContent( + new MemoryStream( + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Unauthorized, (int)SubStatusCodes.Unknown, "Unauthorized", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Unauthorized, rule: {ruleId}"))), + }; + this.SetHttpHeaders(httpResponse, headers, isProxyCall); + httpResponse.Headers.Add(WFConstants.BackendHeaders.LocalLSN, lsn); + return httpResponse; + case FaultInjectionServerErrorType.AadTokenRevoked: + httpResponse = new HttpResponseMessage + { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), + StatusCode = HttpStatusCode.Unauthorized, + Content = new FauntInjectionHttpContent( + new MemoryStream( + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Unauthorized, 5013, "AadTokenRevoked", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: AadTokenRevoked, rule: {ruleId}"))), + }; + this.SetHttpHeaders(httpResponse, headers, isProxyCall); + httpResponse.Headers.Add( + WFConstants.BackendHeaders.SubStatus, + "5013"); + httpResponse.Headers.Add(WFConstants.BackendHeaders.LocalLSN, lsn); + return httpResponse; default: throw new ArgumentException($"Server error type {this.serverErrorType} is not supported"); } } + private bool IsProxyCall(DocumentServiceRequest dsr) + { + string gwUriString = dsr.Headers.Get("FAULTINJECTION_IS_PROXY"); + + return !string.IsNullOrEmpty(gwUriString); + } + + private void SetHttpHeaders( + HttpResponseMessage httpResponse, + INameValueCollection headers, + bool isProxyCall) + { + foreach (string header in headers.AllKeys()) + { + if (header != "FAULTINJECTION_IS_PROXY") + { + httpResponse.Headers.Add(header, headers.Get(header)); + } + } + + if (isProxyCall) + { + httpResponse.Headers.Add(ThinClientConstants.RoutedViaProxy,"1"); + } + } + + private static string GetProxyResponseMessageString( + int statusCode, + int subStatusCode, + string message, + string faultInjectionRuleId) + { + return $"{{\"code\": \"{statusCode}:{subStatusCode}\",\"message\":\"Fault Injection Server Error: {message}, rule: {faultInjectionRuleId}\"}}"; + } + internal class FauntInjectionHttpContent : HttpContent { private readonly Stream content; @@ -536,7 +662,7 @@ protected override bool TryComputeLength(out long length) } } - internal static class FaultInjectionResponseEncoding + internal static class FaultInjectionResponseEncoding { private static readonly UTF8Encoding Encoding = new UTF8Encoding(false); @@ -544,6 +670,11 @@ public static byte[] GetBytes(string value) { return Encoding.GetBytes(value); } + + public static byte[] GetBytesFromHexString(string hexString) + { + return Convert.FromHexString(hexString); + } } } } diff --git a/Microsoft.Azure.Cosmos/FaultInjection/tests/FaultInjectionGatewayModeTests.cs b/Microsoft.Azure.Cosmos/FaultInjection/tests/FaultInjectionGatewayModeTests.cs index 4e36aadd3f..1d2a74bd22 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/tests/FaultInjectionGatewayModeTests.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/tests/FaultInjectionGatewayModeTests.cs @@ -213,6 +213,129 @@ public async Task FIGatewayRegion() } } + [TestMethod] + [Timeout(Timeout)] + [Description("Test Unautharized rule filtering")] + [Owner("ntripician")] + public async Task FIGatewayUnautharizedTest() + { + //create fault injection rule for unauthorized requests + string unauthorizedRuleId = "unauthorizedRule-" + Guid.NewGuid().ToString(); + FaultInjectionRule unauthorizedRule = new FaultInjectionRuleBuilder( + id: unauthorizedRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithConnectionType(FaultInjectionConnectionType.Gateway) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.Unauthorized) + .Build()) + .WithDuration(TimeSpan.FromMinutes(5)) + .Build(); + //disable rule until ready to test + unauthorizedRule.Disable(); + try + { + //create client with fault injection + List rules = new List { unauthorizedRule }; + FaultInjector faultInjector = new FaultInjector(rules); + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConsistencyLevel = ConsistencyLevel.Session, + ConnectionMode = ConnectionMode.Gateway, + Serializer = this.serializer + }; + this.fiClient = new CosmosClient( + this.connectionString, + faultInjector.GetFaultInjectionClientOptions(cosmosClientOptions)); + this.fiDatabase = this.fiClient.GetDatabase(TestCommon.FaultInjectionDatabaseName); + this.fiContainer = this.fiDatabase.GetContainer(TestCommon.FaultInjectionContainerName); + unauthorizedRule.Enable(); + + await this.fiContainer.ReadItemAsync( + "testId2", + new PartitionKey("pk2")); + } + catch (DocumentClientException ex) + { + this.ValidateHitCount(unauthorizedRule, 1); + this.ValidateFaultInjectionRuleApplication( + ex, + (int)HttpStatusCode.Unauthorized, + unauthorizedRule); + } + catch (CosmosException ex) + { + this.ValidateHitCount(unauthorizedRule, 1); + this.ValidateFaultInjectionRuleApplication( + ex, + (int)HttpStatusCode.Unauthorized, + unauthorizedRule); + } + } + + [TestMethod] + [Timeout(Timeout)] + [Description("Test AAD token revoked rule filtering")] + [Owner("ntripician")] + public async Task FIGatewayAADRevokedTest() + { + //create fault injection rule for AAD token revoked requests + string aadRevokedRuleId = "AadRevoked-" + Guid.NewGuid().ToString(); + FaultInjectionRule aadRevokedRule = new FaultInjectionRuleBuilder( + id: aadRevokedRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithConnectionType(FaultInjectionConnectionType.Gateway) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.AadTokenRevoked) + .Build()) + .WithDuration(TimeSpan.FromMinutes(5)) + .Build(); + //disable rule until ready to test + aadRevokedRule.Disable(); + try + { + //create client with fault injection + List rules = new List { aadRevokedRule }; + FaultInjector faultInjector = new FaultInjector(rules); + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConsistencyLevel = ConsistencyLevel.Session, + ConnectionMode = ConnectionMode.Gateway, + Serializer = this.serializer + }; + this.fiClient = new CosmosClient( + this.connectionString, + faultInjector.GetFaultInjectionClientOptions(cosmosClientOptions)); + this.fiDatabase = this.fiClient.GetDatabase(TestCommon.FaultInjectionDatabaseName); + this.fiContainer = this.fiDatabase.GetContainer(TestCommon.FaultInjectionContainerName); + aadRevokedRule.Enable(); + + await this.fiContainer.ReadItemAsync( + "testId2", + new PartitionKey("pk2")); + } + catch (DocumentClientException ex) + { + this.ValidateHitCount(aadRevokedRule, 1); + this.ValidateFaultInjectionRuleApplication( + ex, + (int)HttpStatusCode.Unauthorized, + aadRevokedRule); + } + catch (CosmosException ex) + { + this.ValidateHitCount(aadRevokedRule, 1); + this.ValidateFaultInjectionRuleApplication( + ex, + (int)HttpStatusCode.Unauthorized, + aadRevokedRule); + } + } + + // //Tests to to see if fault injection rules are applied to the correct partitions //We will create a container with a split physical partition (which will happen when a container is provisioned with >10k RU/s) diff --git a/Microsoft.Azure.Cosmos/FaultInjection/tests/FaultInjectionTests.csproj b/Microsoft.Azure.Cosmos/FaultInjection/tests/FaultInjectionTests.csproj index 174ac9b033..263d9dfad4 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/tests/FaultInjectionTests.csproj +++ b/Microsoft.Azure.Cosmos/FaultInjection/tests/FaultInjectionTests.csproj @@ -17,6 +17,7 @@ + diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.53.0.txt b/Microsoft.Azure.Cosmos/contracts/API_3.53.0.txt new file mode 100644 index 0000000000..e70543be37 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.53.0.txt @@ -0,0 +1,1758 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public string Path { get; set; } + public VectorIndexType Type { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.53.1.txt b/Microsoft.Azure.Cosmos/contracts/API_3.53.1.txt new file mode 100644 index 0000000000..e70543be37 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.53.1.txt @@ -0,0 +1,1758 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public string Path { get; set; } + public VectorIndexType Type { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.53.2.txt b/Microsoft.Azure.Cosmos/contracts/API_3.53.2.txt new file mode 100644 index 0000000000..e70543be37 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.53.2.txt @@ -0,0 +1,1758 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public string Path { get; set; } + public VectorIndexType Type { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.54.0-preview.0.txt b/Microsoft.Azure.Cosmos/contracts/API_3.54.0-preview.0.txt new file mode 100644 index 0000000000..fd26baee55 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.54.0-preview.0.txt @@ -0,0 +1,1892 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public class ChangeFeedItem + { + public ChangeFeedItem(); + public T Current { get; set; } + public ChangeFeedMetadata Metadata { get; set; } + public T Previous { get; set; } + } + public class ChangeFeedMetadata + { + public ChangeFeedMetadata(); + public DateTime ConflictResolutionTimestamp { get; } + public bool IsTimeToLiveExpired { get; } + public long Lsn { get; } + public ChangeFeedOperationType OperationType { get; } + public long PreviousLsn { get; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode AllVersionsAndDeletes { get; } + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public enum ChangeFeedOperationType + { + Create = 0, + Delete = 2, + Replace = 1, + } + public sealed class ChangeFeedPolicy + { + public ChangeFeedPolicy(); + public static TimeSpan FullFidelityNoRetention { get; } + public TimeSpan FullFidelityRetention { get; set; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes(string processorName, Container.ChangeFeedHandler> onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> GetPartitionKeyRangesAsync(FeedRange feedRange, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task IsFeedRangePartOfAsync(FeedRange x, FeedRange y, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ChangeFeedPolicy ChangeFeedPolicy { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableRemoteRegionPreferredForSessionRetry { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable ThroughputBucket { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public bool IsClientMetricsEnabled { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public sealed class CosmosDbClientMetrics + { + public CosmosDbClientMetrics(); + public static class HistogramBuckets + { + public static readonly double[] RequestLatencyBuckets; + public static readonly double[] RequestUnitBuckets; + public static readonly double[] RowCountBuckets; + } + public static class NetworkMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Request"; + public const string Version = "1.0.0"; + public static class Description + { + public const string BackendLatency = "Backend Latency (for direct mode)."; + public const string ChannelAquisitionLatency = "The duration of the successfully established outbound TCP connections. i.e. Channel Aquisition Time (for direct mode)."; + public const string Latency = "Duration of client requests."; + public const string ReceivedTimeLatency = "Time spent on 'Received' stage (for direct mode)."; + public const string RequestBodySize = "Size of client request body."; + public const string ResponseBodySize = "Size of client response body."; + public const string TransitTimeLatency = "Time spent on the wire (for direct mode)."; + } + public static class Name + { + public const string BackendLatency = "azure.cosmosdb.client.request.service_duration"; + public const string ChannelAquisitionLatency = "azure.cosmosdb.client.request.channel_aquisition.duration"; + public const string Latency = "azure.cosmosdb.client.request.duration"; + public const string ReceivedTimeLatency = "azure.cosmosdb.client.request.received.duration"; + public const string RequestBodySize = "azure.cosmosdb.client.request.body.size"; + public const string ResponseBodySize = "azure.cosmosdb.client.response.body.size"; + public const string TransitTimeLatency = "azure.cosmosdb.client.request.transit.duration"; + } + public static class Unit + { + public const string Bytes = "bytes"; + public const string Sec = "s"; + } + } + public static class OperationMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Operation"; + public const string Version = "1.0.0"; + public static class Description + { + public const string ActiveInstances = "Number of active SDK client instances."; + public const string Latency = "Total end-to-end duration of the operation"; + public const string RequestCharge = "Total request units per operation (sum of RUs for all requested needed when processing an operation)"; + public const string RowCount = "For feed operations (query, readAll, readMany, change feed) batch operations this meter capture the actual item count in responses from the service"; + } + public static class Name + { + public const string ActiveInstances = "azure.cosmosdb.client.active_instance.count"; + public const string Latency = "db.client.operation.duration"; + public const string RequestCharge = "azure.cosmosdb.client.operation.request_charge"; + public const string RowCount = "db.client.response.returned_rows"; + } + public static class Unit + { + public const string Instance = "{instance}"; + public const string Item = "{item}"; + public const string RequestUnit = "{request_unit}"; + public const string Sec = "s"; + } + } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public class NetworkMetricsOptions + { + public NetworkMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRoutingId { get; set; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public class OperationMetricsOptions + { + public OperationMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRegion { get; set; } + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public Nullable ThroughputBucket { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public int QuantizationByteSize { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public class ChangeFeedPolicyDefinition + { + public ContainerBuilder Attach(); + } + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ChangeFeedPolicyDefinition WithChangeFeedPolicy(TimeSpan retention); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(bool enableRemoteRegionPreferredForSessionRetry); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + public CosmosClientBuilder WithThroughputBucket(int throughputBucket); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + public VectorIndexDefinition WithIndexingSearchListSize(int indexingSearchListSize); + public VectorIndexDefinition WithQuantizationByteSize(int quantizationByteSize); + public VectorIndexDefinition WithVectorIndexShardKey(string[] vectorIndexShardKey); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query, IDictionary namedParameters); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.54.0-preview.1.txt b/Microsoft.Azure.Cosmos/contracts/API_3.54.0-preview.1.txt new file mode 100644 index 0000000000..fd26baee55 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.54.0-preview.1.txt @@ -0,0 +1,1892 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public class ChangeFeedItem + { + public ChangeFeedItem(); + public T Current { get; set; } + public ChangeFeedMetadata Metadata { get; set; } + public T Previous { get; set; } + } + public class ChangeFeedMetadata + { + public ChangeFeedMetadata(); + public DateTime ConflictResolutionTimestamp { get; } + public bool IsTimeToLiveExpired { get; } + public long Lsn { get; } + public ChangeFeedOperationType OperationType { get; } + public long PreviousLsn { get; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode AllVersionsAndDeletes { get; } + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public enum ChangeFeedOperationType + { + Create = 0, + Delete = 2, + Replace = 1, + } + public sealed class ChangeFeedPolicy + { + public ChangeFeedPolicy(); + public static TimeSpan FullFidelityNoRetention { get; } + public TimeSpan FullFidelityRetention { get; set; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes(string processorName, Container.ChangeFeedHandler> onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> GetPartitionKeyRangesAsync(FeedRange feedRange, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task IsFeedRangePartOfAsync(FeedRange x, FeedRange y, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ChangeFeedPolicy ChangeFeedPolicy { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableRemoteRegionPreferredForSessionRetry { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable ThroughputBucket { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public bool IsClientMetricsEnabled { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public sealed class CosmosDbClientMetrics + { + public CosmosDbClientMetrics(); + public static class HistogramBuckets + { + public static readonly double[] RequestLatencyBuckets; + public static readonly double[] RequestUnitBuckets; + public static readonly double[] RowCountBuckets; + } + public static class NetworkMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Request"; + public const string Version = "1.0.0"; + public static class Description + { + public const string BackendLatency = "Backend Latency (for direct mode)."; + public const string ChannelAquisitionLatency = "The duration of the successfully established outbound TCP connections. i.e. Channel Aquisition Time (for direct mode)."; + public const string Latency = "Duration of client requests."; + public const string ReceivedTimeLatency = "Time spent on 'Received' stage (for direct mode)."; + public const string RequestBodySize = "Size of client request body."; + public const string ResponseBodySize = "Size of client response body."; + public const string TransitTimeLatency = "Time spent on the wire (for direct mode)."; + } + public static class Name + { + public const string BackendLatency = "azure.cosmosdb.client.request.service_duration"; + public const string ChannelAquisitionLatency = "azure.cosmosdb.client.request.channel_aquisition.duration"; + public const string Latency = "azure.cosmosdb.client.request.duration"; + public const string ReceivedTimeLatency = "azure.cosmosdb.client.request.received.duration"; + public const string RequestBodySize = "azure.cosmosdb.client.request.body.size"; + public const string ResponseBodySize = "azure.cosmosdb.client.response.body.size"; + public const string TransitTimeLatency = "azure.cosmosdb.client.request.transit.duration"; + } + public static class Unit + { + public const string Bytes = "bytes"; + public const string Sec = "s"; + } + } + public static class OperationMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Operation"; + public const string Version = "1.0.0"; + public static class Description + { + public const string ActiveInstances = "Number of active SDK client instances."; + public const string Latency = "Total end-to-end duration of the operation"; + public const string RequestCharge = "Total request units per operation (sum of RUs for all requested needed when processing an operation)"; + public const string RowCount = "For feed operations (query, readAll, readMany, change feed) batch operations this meter capture the actual item count in responses from the service"; + } + public static class Name + { + public const string ActiveInstances = "azure.cosmosdb.client.active_instance.count"; + public const string Latency = "db.client.operation.duration"; + public const string RequestCharge = "azure.cosmosdb.client.operation.request_charge"; + public const string RowCount = "db.client.response.returned_rows"; + } + public static class Unit + { + public const string Instance = "{instance}"; + public const string Item = "{item}"; + public const string RequestUnit = "{request_unit}"; + public const string Sec = "s"; + } + } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public class NetworkMetricsOptions + { + public NetworkMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRoutingId { get; set; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public class OperationMetricsOptions + { + public OperationMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRegion { get; set; } + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public Nullable ThroughputBucket { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public int QuantizationByteSize { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public class ChangeFeedPolicyDefinition + { + public ContainerBuilder Attach(); + } + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ChangeFeedPolicyDefinition WithChangeFeedPolicy(TimeSpan retention); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(bool enableRemoteRegionPreferredForSessionRetry); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + public CosmosClientBuilder WithThroughputBucket(int throughputBucket); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + public VectorIndexDefinition WithIndexingSearchListSize(int indexingSearchListSize); + public VectorIndexDefinition WithQuantizationByteSize(int quantizationByteSize); + public VectorIndexDefinition WithVectorIndexShardKey(string[] vectorIndexShardKey); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query, IDictionary namedParameters); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.54.0-preview.2.txt b/Microsoft.Azure.Cosmos/contracts/API_3.54.0-preview.2.txt new file mode 100644 index 0000000000..fd26baee55 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.54.0-preview.2.txt @@ -0,0 +1,1892 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public class ChangeFeedItem + { + public ChangeFeedItem(); + public T Current { get; set; } + public ChangeFeedMetadata Metadata { get; set; } + public T Previous { get; set; } + } + public class ChangeFeedMetadata + { + public ChangeFeedMetadata(); + public DateTime ConflictResolutionTimestamp { get; } + public bool IsTimeToLiveExpired { get; } + public long Lsn { get; } + public ChangeFeedOperationType OperationType { get; } + public long PreviousLsn { get; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode AllVersionsAndDeletes { get; } + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public enum ChangeFeedOperationType + { + Create = 0, + Delete = 2, + Replace = 1, + } + public sealed class ChangeFeedPolicy + { + public ChangeFeedPolicy(); + public static TimeSpan FullFidelityNoRetention { get; } + public TimeSpan FullFidelityRetention { get; set; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes(string processorName, Container.ChangeFeedHandler> onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> GetPartitionKeyRangesAsync(FeedRange feedRange, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task IsFeedRangePartOfAsync(FeedRange x, FeedRange y, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ChangeFeedPolicy ChangeFeedPolicy { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableRemoteRegionPreferredForSessionRetry { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable ThroughputBucket { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public bool IsClientMetricsEnabled { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public sealed class CosmosDbClientMetrics + { + public CosmosDbClientMetrics(); + public static class HistogramBuckets + { + public static readonly double[] RequestLatencyBuckets; + public static readonly double[] RequestUnitBuckets; + public static readonly double[] RowCountBuckets; + } + public static class NetworkMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Request"; + public const string Version = "1.0.0"; + public static class Description + { + public const string BackendLatency = "Backend Latency (for direct mode)."; + public const string ChannelAquisitionLatency = "The duration of the successfully established outbound TCP connections. i.e. Channel Aquisition Time (for direct mode)."; + public const string Latency = "Duration of client requests."; + public const string ReceivedTimeLatency = "Time spent on 'Received' stage (for direct mode)."; + public const string RequestBodySize = "Size of client request body."; + public const string ResponseBodySize = "Size of client response body."; + public const string TransitTimeLatency = "Time spent on the wire (for direct mode)."; + } + public static class Name + { + public const string BackendLatency = "azure.cosmosdb.client.request.service_duration"; + public const string ChannelAquisitionLatency = "azure.cosmosdb.client.request.channel_aquisition.duration"; + public const string Latency = "azure.cosmosdb.client.request.duration"; + public const string ReceivedTimeLatency = "azure.cosmosdb.client.request.received.duration"; + public const string RequestBodySize = "azure.cosmosdb.client.request.body.size"; + public const string ResponseBodySize = "azure.cosmosdb.client.response.body.size"; + public const string TransitTimeLatency = "azure.cosmosdb.client.request.transit.duration"; + } + public static class Unit + { + public const string Bytes = "bytes"; + public const string Sec = "s"; + } + } + public static class OperationMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Operation"; + public const string Version = "1.0.0"; + public static class Description + { + public const string ActiveInstances = "Number of active SDK client instances."; + public const string Latency = "Total end-to-end duration of the operation"; + public const string RequestCharge = "Total request units per operation (sum of RUs for all requested needed when processing an operation)"; + public const string RowCount = "For feed operations (query, readAll, readMany, change feed) batch operations this meter capture the actual item count in responses from the service"; + } + public static class Name + { + public const string ActiveInstances = "azure.cosmosdb.client.active_instance.count"; + public const string Latency = "db.client.operation.duration"; + public const string RequestCharge = "azure.cosmosdb.client.operation.request_charge"; + public const string RowCount = "db.client.response.returned_rows"; + } + public static class Unit + { + public const string Instance = "{instance}"; + public const string Item = "{item}"; + public const string RequestUnit = "{request_unit}"; + public const string Sec = "s"; + } + } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public class NetworkMetricsOptions + { + public NetworkMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRoutingId { get; set; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public class OperationMetricsOptions + { + public OperationMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRegion { get; set; } + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public Nullable ThroughputBucket { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public int QuantizationByteSize { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public class ChangeFeedPolicyDefinition + { + public ContainerBuilder Attach(); + } + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ChangeFeedPolicyDefinition WithChangeFeedPolicy(TimeSpan retention); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(bool enableRemoteRegionPreferredForSessionRetry); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + public CosmosClientBuilder WithThroughputBucket(int throughputBucket); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + public VectorIndexDefinition WithIndexingSearchListSize(int indexingSearchListSize); + public VectorIndexDefinition WithQuantizationByteSize(int quantizationByteSize); + public VectorIndexDefinition WithVectorIndexShardKey(string[] vectorIndexShardKey); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query, IDictionary namedParameters); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.54.0.txt b/Microsoft.Azure.Cosmos/contracts/API_3.54.0.txt new file mode 100644 index 0000000000..2b522a80fc --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.54.0.txt @@ -0,0 +1,1764 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndiaSouthCentral = "India South Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NortheastUS5 = "Northeast US 5"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SingaporeCentral = "Singapore Central"; + public const string SingaporeNorth = "Singapore North"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float16 = 3, + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public string Path { get; set; } + public VectorIndexType Type { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.55.0-preview.0.txt b/Microsoft.Azure.Cosmos/contracts/API_3.55.0-preview.0.txt new file mode 100644 index 0000000000..b3f46be2fc --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.55.0-preview.0.txt @@ -0,0 +1,1897 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public class ChangeFeedItem + { + public ChangeFeedItem(); + public T Current { get; set; } + public ChangeFeedMetadata Metadata { get; set; } + public T Previous { get; set; } + } + public class ChangeFeedMetadata + { + public ChangeFeedMetadata(); + public DateTime ConflictResolutionTimestamp { get; } + public bool IsTimeToLiveExpired { get; } + public long Lsn { get; } + public ChangeFeedOperationType OperationType { get; } + public long PreviousLsn { get; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode AllVersionsAndDeletes { get; } + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public enum ChangeFeedOperationType + { + Create = 0, + Delete = 2, + Replace = 1, + } + public sealed class ChangeFeedPolicy + { + public ChangeFeedPolicy(); + public static TimeSpan FullFidelityNoRetention { get; } + public TimeSpan FullFidelityRetention { get; set; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes(string processorName, Container.ChangeFeedHandler> onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> GetPartitionKeyRangesAsync(FeedRange feedRange, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task IsFeedRangePartOfAsync(FeedRange x, FeedRange y, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ChangeFeedPolicy ChangeFeedPolicy { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableRemoteRegionPreferredForSessionRetry { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable ThroughputBucket { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public bool IsClientMetricsEnabled { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public sealed class CosmosDbClientMetrics + { + public CosmosDbClientMetrics(); + public static class HistogramBuckets + { + public static readonly double[] RequestLatencyBuckets; + public static readonly double[] RequestUnitBuckets; + public static readonly double[] RowCountBuckets; + } + public static class NetworkMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Request"; + public const string Version = "1.0.0"; + public static class Description + { + public const string BackendLatency = "Backend Latency (for direct mode)."; + public const string ChannelAquisitionLatency = "The duration of the successfully established outbound TCP connections. i.e. Channel Aquisition Time (for direct mode)."; + public const string Latency = "Duration of client requests."; + public const string ReceivedTimeLatency = "Time spent on 'Received' stage (for direct mode)."; + public const string RequestBodySize = "Size of client request body."; + public const string ResponseBodySize = "Size of client response body."; + public const string TransitTimeLatency = "Time spent on the wire (for direct mode)."; + } + public static class Name + { + public const string BackendLatency = "azure.cosmosdb.client.request.service_duration"; + public const string ChannelAquisitionLatency = "azure.cosmosdb.client.request.channel_aquisition.duration"; + public const string Latency = "azure.cosmosdb.client.request.duration"; + public const string ReceivedTimeLatency = "azure.cosmosdb.client.request.received.duration"; + public const string RequestBodySize = "azure.cosmosdb.client.request.body.size"; + public const string ResponseBodySize = "azure.cosmosdb.client.response.body.size"; + public const string TransitTimeLatency = "azure.cosmosdb.client.request.transit.duration"; + } + public static class Unit + { + public const string Bytes = "bytes"; + public const string Sec = "s"; + } + } + public static class OperationMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Operation"; + public const string Version = "1.0.0"; + public static class Description + { + public const string ActiveInstances = "Number of active SDK client instances."; + public const string Latency = "Total end-to-end duration of the operation"; + public const string RequestCharge = "Total request units per operation (sum of RUs for all requested needed when processing an operation)"; + public const string RowCount = "For feed operations (query, readAll, readMany, change feed) batch operations this meter capture the actual item count in responses from the service"; + } + public static class Name + { + public const string ActiveInstances = "azure.cosmosdb.client.active_instance.count"; + public const string Latency = "db.client.operation.duration"; + public const string RequestCharge = "azure.cosmosdb.client.operation.request_charge"; + public const string RowCount = "db.client.response.returned_rows"; + } + public static class Unit + { + public const string Instance = "{instance}"; + public const string Item = "{item}"; + public const string RequestUnit = "{request_unit}"; + public const string Sec = "s"; + } + } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public class NetworkMetricsOptions + { + public NetworkMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRoutingId { get; set; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public class OperationMetricsOptions + { + public OperationMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRegion { get; set; } + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndiaSouthCentral = "India South Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NortheastUS5 = "Northeast US 5"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SingaporeCentral = "Singapore Central"; + public const string SingaporeNorth = "Singapore North"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public Nullable ThroughputBucket { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float16 = 3, + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public int QuantizationByteSize { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public class ChangeFeedPolicyDefinition + { + public ContainerBuilder Attach(); + } + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ChangeFeedPolicyDefinition WithChangeFeedPolicy(TimeSpan retention); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(bool enableRemoteRegionPreferredForSessionRetry); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + public CosmosClientBuilder WithThroughputBucket(int throughputBucket); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + public VectorIndexDefinition WithIndexingSearchListSize(int indexingSearchListSize); + public VectorIndexDefinition WithQuantizationByteSize(int quantizationByteSize); + public VectorIndexDefinition WithVectorIndexShardKey(string[] vectorIndexShardKey); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query, IDictionary namedParameters); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.55.0.txt b/Microsoft.Azure.Cosmos/contracts/API_3.55.0.txt new file mode 100644 index 0000000000..19601917c5 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.55.0.txt @@ -0,0 +1,1766 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndiaSouthCentral = "India South Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NortheastUS5 = "Northeast US 5"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SingaporeCentral = "Singapore Central"; + public const string SingaporeNorth = "Singapore North"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float16 = 3, + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.56.0-preview.0.txt b/Microsoft.Azure.Cosmos/contracts/API_3.56.0-preview.0.txt new file mode 100644 index 0000000000..10af2c2229 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.56.0-preview.0.txt @@ -0,0 +1,1899 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public class ChangeFeedItem + { + public ChangeFeedItem(); + public T Current { get; set; } + public ChangeFeedMetadata Metadata { get; set; } + public T Previous { get; set; } + } + public class ChangeFeedMetadata + { + public ChangeFeedMetadata(); + public Nullable ConflictResolutionTimestamp { get; } + public string Id { get; } + public bool IsTimeToLiveExpired { get; } + public long Lsn { get; } + public ChangeFeedOperationType OperationType { get; } + public Dictionary PartitionKey { get; } + public long PreviousLsn { get; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode AllVersionsAndDeletes { get; } + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public enum ChangeFeedOperationType + { + Create = 0, + Delete = 2, + Replace = 1, + } + public sealed class ChangeFeedPolicy + { + public ChangeFeedPolicy(); + public static TimeSpan FullFidelityNoRetention { get; } + public TimeSpan FullFidelityRetention { get; set; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes(string processorName, Container.ChangeFeedHandler> onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> GetPartitionKeyRangesAsync(FeedRange feedRange, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task IsFeedRangePartOfAsync(FeedRange x, FeedRange y, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ChangeFeedPolicy ChangeFeedPolicy { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableRemoteRegionPreferredForSessionRetry { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable ThroughputBucket { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public bool IsClientMetricsEnabled { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public sealed class CosmosDbClientMetrics + { + public CosmosDbClientMetrics(); + public static class HistogramBuckets + { + public static readonly double[] RequestLatencyBuckets; + public static readonly double[] RequestUnitBuckets; + public static readonly double[] RowCountBuckets; + } + public static class NetworkMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Request"; + public const string Version = "1.0.0"; + public static class Description + { + public const string BackendLatency = "Backend Latency (for direct mode)."; + public const string ChannelAquisitionLatency = "The duration of the successfully established outbound TCP connections. i.e. Channel Aquisition Time (for direct mode)."; + public const string Latency = "Duration of client requests."; + public const string ReceivedTimeLatency = "Time spent on 'Received' stage (for direct mode)."; + public const string RequestBodySize = "Size of client request body."; + public const string ResponseBodySize = "Size of client response body."; + public const string TransitTimeLatency = "Time spent on the wire (for direct mode)."; + } + public static class Name + { + public const string BackendLatency = "azure.cosmosdb.client.request.service_duration"; + public const string ChannelAquisitionLatency = "azure.cosmosdb.client.request.channel_aquisition.duration"; + public const string Latency = "azure.cosmosdb.client.request.duration"; + public const string ReceivedTimeLatency = "azure.cosmosdb.client.request.received.duration"; + public const string RequestBodySize = "azure.cosmosdb.client.request.body.size"; + public const string ResponseBodySize = "azure.cosmosdb.client.response.body.size"; + public const string TransitTimeLatency = "azure.cosmosdb.client.request.transit.duration"; + } + public static class Unit + { + public const string Bytes = "bytes"; + public const string Sec = "s"; + } + } + public static class OperationMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Operation"; + public const string Version = "1.0.0"; + public static class Description + { + public const string ActiveInstances = "Number of active SDK client instances."; + public const string Latency = "Total end-to-end duration of the operation"; + public const string RequestCharge = "Total request units per operation (sum of RUs for all requested needed when processing an operation)"; + public const string RowCount = "For feed operations (query, readAll, readMany, change feed) batch operations this meter capture the actual item count in responses from the service"; + } + public static class Name + { + public const string ActiveInstances = "azure.cosmosdb.client.active_instance.count"; + public const string Latency = "db.client.operation.duration"; + public const string RequestCharge = "azure.cosmosdb.client.operation.request_charge"; + public const string RowCount = "db.client.response.returned_rows"; + } + public static class Unit + { + public const string Instance = "{instance}"; + public const string Item = "{item}"; + public const string RequestUnit = "{request_unit}"; + public const string Sec = "s"; + } + } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public class NetworkMetricsOptions + { + public NetworkMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRoutingId { get; set; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public class OperationMetricsOptions + { + public OperationMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRegion { get; set; } + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndiaSouthCentral = "India South Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NortheastUS5 = "Northeast US 5"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SingaporeCentral = "Singapore Central"; + public const string SingaporeNorth = "Singapore North"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public Nullable ThroughputBucket { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float16 = 3, + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public int QuantizationByteSize { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public class ChangeFeedPolicyDefinition + { + public ContainerBuilder Attach(); + } + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ChangeFeedPolicyDefinition WithChangeFeedPolicy(TimeSpan retention); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(bool enableRemoteRegionPreferredForSessionRetry); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + public CosmosClientBuilder WithThroughputBucket(int throughputBucket); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + public VectorIndexDefinition WithIndexingSearchListSize(int indexingSearchListSize); + public VectorIndexDefinition WithQuantizationByteSize(int quantizationByteSize); + public VectorIndexDefinition WithVectorIndexShardKey(string[] vectorIndexShardKey); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query, IDictionary namedParameters); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.56.0.txt b/Microsoft.Azure.Cosmos/contracts/API_3.56.0.txt new file mode 100644 index 0000000000..19601917c5 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.56.0.txt @@ -0,0 +1,1766 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndiaSouthCentral = "India South Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NortheastUS5 = "Northeast US 5"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SingaporeCentral = "Singapore Central"; + public const string SingaporeNorth = "Singapore North"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float16 = 3, + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.57.0-preview.0.txt b/Microsoft.Azure.Cosmos/contracts/API_3.57.0-preview.0.txt new file mode 100644 index 0000000000..9d46f9c8c9 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.57.0-preview.0.txt @@ -0,0 +1,1914 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public class ChangeFeedItem + { + public ChangeFeedItem(); + public T Current { get; set; } + public ChangeFeedMetadata Metadata { get; set; } + public T Previous { get; set; } + } + public class ChangeFeedMetadata + { + public ChangeFeedMetadata(); + public Nullable ConflictResolutionTimestamp { get; } + public string Id { get; } + public bool IsTimeToLiveExpired { get; } + public long Lsn { get; } + public ChangeFeedOperationType OperationType { get; } + public Dictionary PartitionKey { get; } + public long PreviousLsn { get; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode AllVersionsAndDeletes { get; } + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public enum ChangeFeedOperationType + { + Create = 0, + Delete = 2, + Replace = 1, + } + public sealed class ChangeFeedPolicy + { + public ChangeFeedPolicy(); + public static TimeSpan FullFidelityNoRetention { get; } + public TimeSpan FullFidelityRetention { get; set; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes(string processorName, Container.ChangeFeedHandler> onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> GetPartitionKeyRangesAsync(FeedRange feedRange, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task IsFeedRangePartOfAsync(FeedRange x, FeedRange y, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task SemanticRerankAsync(string rerankContext, IEnumerable documents, IDictionary options=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ChangeFeedPolicy ChangeFeedPolicy { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableRemoteRegionPreferredForSessionRetry { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable ThroughputBucket { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public bool IsClientMetricsEnabled { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public sealed class CosmosDbClientMetrics + { + public CosmosDbClientMetrics(); + public static class HistogramBuckets + { + public static readonly double[] RequestLatencyBuckets; + public static readonly double[] RequestUnitBuckets; + public static readonly double[] RowCountBuckets; + } + public static class NetworkMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Request"; + public const string Version = "1.0.0"; + public static class Description + { + public const string BackendLatency = "Backend Latency (for direct mode)."; + public const string ChannelAquisitionLatency = "The duration of the successfully established outbound TCP connections. i.e. Channel Aquisition Time (for direct mode)."; + public const string Latency = "Duration of client requests."; + public const string ReceivedTimeLatency = "Time spent on 'Received' stage (for direct mode)."; + public const string RequestBodySize = "Size of client request body."; + public const string ResponseBodySize = "Size of client response body."; + public const string TransitTimeLatency = "Time spent on the wire (for direct mode)."; + } + public static class Name + { + public const string BackendLatency = "azure.cosmosdb.client.request.service_duration"; + public const string ChannelAquisitionLatency = "azure.cosmosdb.client.request.channel_aquisition.duration"; + public const string Latency = "azure.cosmosdb.client.request.duration"; + public const string ReceivedTimeLatency = "azure.cosmosdb.client.request.received.duration"; + public const string RequestBodySize = "azure.cosmosdb.client.request.body.size"; + public const string ResponseBodySize = "azure.cosmosdb.client.response.body.size"; + public const string TransitTimeLatency = "azure.cosmosdb.client.request.transit.duration"; + } + public static class Unit + { + public const string Bytes = "bytes"; + public const string Sec = "s"; + } + } + public static class OperationMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Operation"; + public const string Version = "1.0.0"; + public static class Description + { + public const string ActiveInstances = "Number of active SDK client instances."; + public const string Latency = "Total end-to-end duration of the operation"; + public const string RequestCharge = "Total request units per operation (sum of RUs for all requested needed when processing an operation)"; + public const string RowCount = "For feed operations (query, readAll, readMany, change feed) batch operations this meter capture the actual item count in responses from the service"; + } + public static class Name + { + public const string ActiveInstances = "azure.cosmosdb.client.active_instance.count"; + public const string Latency = "db.client.operation.duration"; + public const string RequestCharge = "azure.cosmosdb.client.operation.request_charge"; + public const string RowCount = "db.client.response.returned_rows"; + } + public static class Unit + { + public const string Instance = "{instance}"; + public const string Item = "{item}"; + public const string RequestUnit = "{request_unit}"; + public const string Sec = "s"; + } + } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public class NetworkMetricsOptions + { + public NetworkMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRoutingId { get; set; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public class OperationMetricsOptions + { + public OperationMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRegion { get; set; } + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndiaSouthCentral = "India South Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NortheastUS5 = "Northeast US 5"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SingaporeCentral = "Singapore Central"; + public const string SingaporeNorth = "Singapore North"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public Nullable ThroughputBucket { get; set; } + public RequestOptions ShallowCopy(); + } + public class RerankScore + { + public RerankScore(object document, double score, int index); + public object Document { get; } + public int Index { get; } + public double Score { get; } + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public class SemanticRerankResult + { + public HttpResponseHeaders Headers { get; } + public Dictionary Latency { get; } + public IReadOnlyList RerankScores { get; } + public Dictionary TokenUseage { get; } + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float16 = 3, + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public int QuantizationByteSize { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public class ChangeFeedPolicyDefinition + { + public ContainerBuilder Attach(); + } + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ChangeFeedPolicyDefinition WithChangeFeedPolicy(TimeSpan retention); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(bool enableRemoteRegionPreferredForSessionRetry); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + public CosmosClientBuilder WithThroughputBucket(int throughputBucket); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + public VectorIndexDefinition WithIndexingSearchListSize(int indexingSearchListSize); + public VectorIndexDefinition WithQuantizationByteSize(int quantizationByteSize); + public VectorIndexDefinition WithVectorIndexShardKey(string[] vectorIndexShardKey); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query, IDictionary namedParameters); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.57.0-preview.1.txt b/Microsoft.Azure.Cosmos/contracts/API_3.57.0-preview.1.txt new file mode 100644 index 0000000000..d90d314a47 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.57.0-preview.1.txt @@ -0,0 +1,1914 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public class ChangeFeedItem + { + public ChangeFeedItem(); + public T Current { get; set; } + public ChangeFeedMetadata Metadata { get; set; } + public T Previous { get; set; } + } + public class ChangeFeedMetadata + { + public ChangeFeedMetadata(); + public Nullable ConflictResolutionTimestamp { get; } + public string Id { get; } + public bool IsTimeToLiveExpired { get; } + public long Lsn { get; } + public ChangeFeedOperationType OperationType { get; } + public Dictionary PartitionKey { get; } + public long PreviousLsn { get; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode AllVersionsAndDeletes { get; } + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public enum ChangeFeedOperationType + { + Create = 0, + Delete = 2, + Replace = 1, + } + public sealed class ChangeFeedPolicy + { + public ChangeFeedPolicy(); + public static TimeSpan FullFidelityNoRetention { get; } + public TimeSpan FullFidelityRetention { get; set; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes(string processorName, Container.ChangeFeedHandler> onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> GetPartitionKeyRangesAsync(FeedRange feedRange, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task IsFeedRangePartOfAsync(FeedRange x, FeedRange y, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task SemanticRerankAsync(string rerankContext, IEnumerable documents, IDictionary options=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ChangeFeedPolicy ChangeFeedPolicy { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableRemoteRegionPreferredForSessionRetry { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable ThroughputBucket { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public bool IsClientMetricsEnabled { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public sealed class CosmosDbClientMetrics + { + public CosmosDbClientMetrics(); + public static class HistogramBuckets + { + public static readonly double[] RequestLatencyBuckets; + public static readonly double[] RequestUnitBuckets; + public static readonly double[] RowCountBuckets; + } + public static class NetworkMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Request"; + public const string Version = "1.0.0"; + public static class Description + { + public const string BackendLatency = "Backend Latency (for direct mode)."; + public const string ChannelAquisitionLatency = "The duration of the successfully established outbound TCP connections. i.e. Channel Aquisition Time (for direct mode)."; + public const string Latency = "Duration of client requests."; + public const string ReceivedTimeLatency = "Time spent on 'Received' stage (for direct mode)."; + public const string RequestBodySize = "Size of client request body."; + public const string ResponseBodySize = "Size of client response body."; + public const string TransitTimeLatency = "Time spent on the wire (for direct mode)."; + } + public static class Name + { + public const string BackendLatency = "azure.cosmosdb.client.request.service_duration"; + public const string ChannelAquisitionLatency = "azure.cosmosdb.client.request.channel_aquisition.duration"; + public const string Latency = "azure.cosmosdb.client.request.duration"; + public const string ReceivedTimeLatency = "azure.cosmosdb.client.request.received.duration"; + public const string RequestBodySize = "azure.cosmosdb.client.request.body.size"; + public const string ResponseBodySize = "azure.cosmosdb.client.response.body.size"; + public const string TransitTimeLatency = "azure.cosmosdb.client.request.transit.duration"; + } + public static class Unit + { + public const string Bytes = "bytes"; + public const string Sec = "s"; + } + } + public static class OperationMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Operation"; + public const string Version = "1.0.0"; + public static class Description + { + public const string ActiveInstances = "Number of active SDK client instances."; + public const string Latency = "Total end-to-end duration of the operation"; + public const string RequestCharge = "Total request units per operation (sum of RUs for all requested needed when processing an operation)"; + public const string RowCount = "For feed operations (query, readAll, readMany, change feed) batch operations this meter capture the actual item count in responses from the service"; + } + public static class Name + { + public const string ActiveInstances = "azure.cosmosdb.client.active_instance.count"; + public const string Latency = "db.client.operation.duration"; + public const string RequestCharge = "azure.cosmosdb.client.operation.request_charge"; + public const string RowCount = "db.client.response.returned_rows"; + } + public static class Unit + { + public const string Instance = "{instance}"; + public const string Item = "{item}"; + public const string RequestUnit = "{request_unit}"; + public const string Sec = "s"; + } + } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public class NetworkMetricsOptions + { + public NetworkMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRoutingId { get; set; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public class OperationMetricsOptions + { + public OperationMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRegion { get; set; } + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndiaSouthCentral = "India South Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NortheastUS5 = "Northeast US 5"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SingaporeCentral = "Singapore Central"; + public const string SingaporeNorth = "Singapore North"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public Nullable ThroughputBucket { get; set; } + public RequestOptions ShallowCopy(); + } + public class RerankScore + { + public RerankScore(string document, double score, int index); + public string Document { get; } + public int Index { get; } + public double Score { get; } + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public class SemanticRerankResult + { + public HttpResponseHeaders Headers { get; } + public Dictionary Latency { get; } + public IReadOnlyList RerankScores { get; } + public Dictionary TokenUseage { get; } + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float16 = 3, + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public int QuantizationByteSize { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public class ChangeFeedPolicyDefinition + { + public ContainerBuilder Attach(); + } + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ChangeFeedPolicyDefinition WithChangeFeedPolicy(TimeSpan retention); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(bool enableRemoteRegionPreferredForSessionRetry); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + public CosmosClientBuilder WithThroughputBucket(int throughputBucket); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + public VectorIndexDefinition WithIndexingSearchListSize(int indexingSearchListSize); + public VectorIndexDefinition WithQuantizationByteSize(int quantizationByteSize); + public VectorIndexDefinition WithVectorIndexShardKey(string[] vectorIndexShardKey); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query, IDictionary namedParameters); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.57.0.txt b/Microsoft.Azure.Cosmos/contracts/API_3.57.0.txt new file mode 100644 index 0000000000..a1fa19e408 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.57.0.txt @@ -0,0 +1,1768 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndiaSouthCentral = "India South Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NortheastUS5 = "Northeast US 5"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SingaporeCentral = "Singapore Central"; + public const string SingaporeNorth = "Singapore North"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float16 = 3, + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static bool ArrayContainsAll(this IEnumerable obj, params object[] values); + public static bool ArrayContainsAny(this IEnumerable obj, params object[] values); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.58.0-preview.0.txt b/Microsoft.Azure.Cosmos/contracts/API_3.58.0-preview.0.txt new file mode 100644 index 0000000000..af57dd8355 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.58.0-preview.0.txt @@ -0,0 +1,1916 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public abstract class AvailabilityStrategy + { + public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold, Nullable thresholdStep, bool enableMultiWriteRegionHedge=false); + public static AvailabilityStrategy DisabledStrategy(); + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public class ChangeFeedItem + { + public ChangeFeedItem(); + public T Current { get; set; } + public ChangeFeedMetadata Metadata { get; set; } + public T Previous { get; set; } + } + public class ChangeFeedMetadata + { + public ChangeFeedMetadata(); + public DateTime ConflictResolutionTimestamp { get; } + public string Id { get; } + public bool IsTimeToLiveExpired { get; } + public long Lsn { get; } + public ChangeFeedOperationType OperationType { get; } + public Dictionary PartitionKey { get; } + public long PreviousLsn { get; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode AllVersionsAndDeletes { get; } + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public enum ChangeFeedOperationType + { + Create = 0, + Delete = 2, + Replace = 1, + } + public sealed class ChangeFeedPolicy + { + public ChangeFeedPolicy(); + public static TimeSpan FullFidelityNoRetention { get; } + public TimeSpan FullFidelityRetention { get; set; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract FeedRange FeedRange { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public virtual Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes(string processorName, Container.ChangeFeedHandler> onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> GetPartitionKeyRangesAsync(FeedRange feedRange, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task IsFeedRangePartOfAsync(FeedRange x, FeedRange y, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task SemanticRerankAsync(string rerankContext, IEnumerable documents, IDictionary options=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ChangeFeedPolicy ChangeFeedPolicy { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public FullTextPolicy FullTextPolicy { get; set; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + public VectorEmbeddingPolicy VectorEmbeddingPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public IEnumerable AccountInitializationCustomEndpoints { get; set; } + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableRemoteRegionPreferredForSessionRetry { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public IFaultInjector FaultInjector { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public Nullable PriorityLevel { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable ThroughputBucket { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public JsonSerializerOptions UseSystemTextJsonSerializerWithOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public class CosmosClientTelemetryOptions + { + public CosmosClientTelemetryOptions(); + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public bool DisableDistributedTracing { get; set; } + public bool DisableSendingMetricsToService { get; set; } + public bool IsClientMetricsEnabled { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public QueryTextMode QueryTextMode { get; set; } + } + public sealed class CosmosDbClientMetrics + { + public CosmosDbClientMetrics(); + public static class HistogramBuckets + { + public static readonly double[] RequestLatencyBuckets; + public static readonly double[] RequestUnitBuckets; + public static readonly double[] RowCountBuckets; + } + public static class NetworkMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Request"; + public const string Version = "1.0.0"; + public static class Description + { + public const string BackendLatency = "Backend Latency (for direct mode)."; + public const string ChannelAquisitionLatency = "The duration of the successfully established outbound TCP connections. i.e. Channel Aquisition Time (for direct mode)."; + public const string Latency = "Duration of client requests."; + public const string ReceivedTimeLatency = "Time spent on 'Received' stage (for direct mode)."; + public const string RequestBodySize = "Size of client request body."; + public const string ResponseBodySize = "Size of client response body."; + public const string TransitTimeLatency = "Time spent on the wire (for direct mode)."; + } + public static class Name + { + public const string BackendLatency = "azure.cosmosdb.client.request.service_duration"; + public const string ChannelAquisitionLatency = "azure.cosmosdb.client.request.channel_aquisition.duration"; + public const string Latency = "azure.cosmosdb.client.request.duration"; + public const string ReceivedTimeLatency = "azure.cosmosdb.client.request.received.duration"; + public const string RequestBodySize = "azure.cosmosdb.client.request.body.size"; + public const string ResponseBodySize = "azure.cosmosdb.client.response.body.size"; + public const string TransitTimeLatency = "azure.cosmosdb.client.request.transit.duration"; + } + public static class Unit + { + public const string Bytes = "bytes"; + public const string Sec = "s"; + } + } + public static class OperationMetrics + { + public const string MeterName = "Azure.Cosmos.Client.Operation"; + public const string Version = "1.0.0"; + public static class Description + { + public const string ActiveInstances = "Number of active SDK client instances."; + public const string Latency = "Total end-to-end duration of the operation"; + public const string RequestCharge = "Total request units per operation (sum of RUs for all requested needed when processing an operation)"; + public const string RowCount = "For feed operations (query, readAll, readMany, change feed) batch operations this meter capture the actual item count in responses from the service"; + } + public static class Name + { + public const string ActiveInstances = "azure.cosmosdb.client.active_instance.count"; + public const string Latency = "db.client.operation.duration"; + public const string RequestCharge = "azure.cosmosdb.client.operation.request_charge"; + public const string RowCount = "db.client.response.returned_rows"; + } + public static class Unit + { + public const string Instance = "{instance}"; + public const string Item = "{item}"; + public const string RequestUnit = "{request_unit}"; + public const string Sec = "s"; + } + } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual ServerSideCumulativeMetrics GetQueryMetrics(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception, ICloneable + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public object Clone(); + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public abstract class CosmosLinqSerializer : CosmosSerializer + { + protected CosmosLinqSerializer(); + public abstract string SerializeMemberName(MemberInfo memberInfo); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException, ICloneable + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public object Clone(); + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public class CosmosThresholdOptions + { + public CosmosThresholdOptions(); + public TimeSpan NonPointOperationLatencyThreshold { get; set; } + public Nullable PayloadSizeThresholdInBytes { get; set; } + public TimeSpan PointOperationLatencyThreshold { get; set; } + public Nullable RequestChargeThreshold { get; set; } + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public enum DistanceFunction + { + Cosine = 1, + DotProduct = 2, + Euclidean = 0, + } + public class Embedding : IEquatable + { + public Embedding(); + public VectorDataType DataType { get; set; } + public int Dimensions { get; set; } + public DistanceFunction DistanceFunction { get; set; } + public string Path { get; set; } + public bool Equals(Embedding that); + public void ValidateEmbeddingPath(); + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public virtual string QueryAdvice { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class FullTextIndexPath + { + public FullTextIndexPath(); + public string Path { get; set; } + } + public class FullTextPath : IEquatable + { + public FullTextPath(); + public string Language { get; set; } + public string Path { get; set; } + public bool Equals(FullTextPath that); + public void ValidateFullTextPath(); + } + public sealed class FullTextPolicy + { + public FullTextPolicy(); + public string DefaultLanguage { get; set; } + public Collection FullTextPaths { get; set; } + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection FullTextIndexes { get; set; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + public Collection VectorIndexes { get; set; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public class NetworkMetricsOptions + { + public NetworkMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRoutingId { get; set; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public class OperationMetricsOptions + { + public OperationMetricsOptions(); + public IDictionary CustomDimensions { get; set; } + public Nullable IncludeRegion { get; set; } + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable PopulateQueryAdvice { get; set; } + public QueryTextMode QueryTextMode { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public enum QueryTextMode + { + All = 2, + None = 0, + ParameterizedOnly = 1, + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string AustriaEast = "Austria East"; + public const string BelgiumCentral = "Belgium Central"; + public const string BleuFranceCentral = "Bleu France Central"; + public const string BleuFranceSouth = "Bleu France South"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChileCentral = "Chile Central"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string DelosCloudGermanyCentral = "Delos Cloud Germany Central"; + public const string DelosCloudGermanyNorth = "Delos Cloud Germany North"; + public const string DenmarkEast = "Denmark East"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUS3 = "East US 3"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyNorth = "Germany North"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IndiaSouthCentral = "India South Central"; + public const string IndonesiaCentral = "Indonesia Central"; + public const string IsraelCentral = "Israel Central"; + public const string IsraelNorthwest = "Israel Northwest"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string MalaysiaWest = "Malaysia West"; + public const string MexicoCentral = "Mexico Central"; + public const string NewZealandNorth = "New Zealand North"; + public const string NorthCentralUS = "North Central US"; + public const string NortheastUS5 = "Northeast US 5"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SingaporeCentral = "Singapore Central"; + public const string SingaporeNorth = "Singapore North"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SouthCentralUS2 = "South Central US 2"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastUS = "Southeast US"; + public const string SoutheastUS3 = "Southeast US 3"; + public const string SoutheastUS5 = "Southeast US 5"; + public const string SouthIndia = "South India"; + public const string SouthwestUS = "Southwest US"; + public const string SpainCentral = "Spain Central"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string TaiwanNorth = "Taiwan North"; + public const string TaiwanNorthwest = "Taiwan Northwest"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string USSecWestCentral = "USSec West Central"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } + public CosmosThresholdOptions CosmosThresholdOptions { get; set; } + public List ExcludeRegions { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public NetworkMetricsOptions NetworkMetricsOptions { get; set; } + public OperationMetricsOptions OperationMetricsOptions { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public Nullable ThroughputBucket { get; set; } + public RequestOptions ShallowCopy(); + } + public class RerankScore + { + public RerankScore(string document, double score, int index); + public string Document { get; } + public int Index { get; } + public double Score { get; } + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public class SemanticRerankResult + { + public HttpResponseHeaders Headers { get; } + public Dictionary Latency { get; } + public IReadOnlyList RerankScores { get; } + public Dictionary TokenUseage { get; } + } + public abstract class ServerSideCumulativeMetrics + { + protected ServerSideCumulativeMetrics(); + public abstract ServerSideMetrics CumulativeMetrics { get; } + public abstract IReadOnlyList PartitionedMetrics { get; } + public abstract double TotalRequestCharge { get; } + } + public abstract class ServerSideMetrics + { + protected ServerSideMetrics(); + public abstract TimeSpan DocumentLoadTime { get; } + public abstract TimeSpan DocumentWriteTime { get; } + public abstract double IndexHitRatio { get; } + public abstract TimeSpan IndexLookupTime { get; } + public abstract long OutputDocumentCount { get; } + public abstract long OutputDocumentSize { get; } + public abstract TimeSpan QueryPreparationTime { get; } + public abstract long RetrievedDocumentCount { get; } + public abstract long RetrievedDocumentSize { get; } + public abstract TimeSpan RuntimeExecutionTime { get; } + public abstract TimeSpan TotalTime { get; } + public abstract TimeSpan VMExecutionTime { get; } + } + public abstract class ServerSidePartitionedMetrics + { + protected ServerSidePartitionedMetrics(); + public abstract string FeedRange { get; } + public abstract Nullable PartitionKeyRangeId { get; } + public abstract double RequestCharge { get; } + public abstract ServerSideMetrics ServerSideMetrics { get; } + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } + public enum VectorDataType + { + Float16 = 3, + Float32 = 0, + Int8 = 2, + Uint8 = 1, + } + public sealed class VectorEmbeddingPolicy + { + public readonly Collection Embeddings; + public VectorEmbeddingPolicy(Collection embeddings); + } + public sealed class VectorIndexPath + { + public VectorIndexPath(); + public int IndexingSearchListSize { get; set; } + public string Path { get; set; } + public int QuantizationByteSize { get; set; } + public VectorIndexType Type { get; set; } + public string[] VectorIndexShardKey { get; set; } + } + public enum VectorIndexType + { + DiskANN = 1, + Flat = 0, + QuantizedFlat = 2, + } +} +namespace Microsoft.Azure.Cosmos.FaultInjection +{ + public interface IFaultInjector + { + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public class ChangeFeedPolicyDefinition + { + public ContainerBuilder Attach(); + } + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ChangeFeedPolicyDefinition WithChangeFeedPolicy(TimeSpan retention); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public FullTextPolicyDefinition WithFullTextPolicy(string defaultLanguage, Collection fullTextPaths); + public UniqueKeyDefinition WithUniqueKey(); + public VectorEmbeddingPolicyDefinition WithVectorEmbeddingPolicy(Collection embeddings); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithAvailabilityStrategy(AvailabilityStrategy strategy); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithClientTelemetryOptions(CosmosClientTelemetryOptions options); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable customAccountEndpoints); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(bool enableRemoteRegionPreferredForSessionRetry); + public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithSystemTextJsonSerializerOptions(JsonSerializerOptions serializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + public CosmosClientBuilder WithThroughputBucket(int throughputBucket); + } + public class FullTextIndexDefinition + { + public FullTextIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public FullTextIndexDefinition Path(string path); + } + public class FullTextPolicyDefinition + { + public FullTextPolicyDefinition(ContainerBuilder parent, string defaultLanguage, Collection fullTextPaths, Action attachCallback); + public ContainerBuilder Attach(); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public FullTextIndexDefinition> WithFullTextIndex(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + public VectorIndexDefinition> WithVectorIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } + public class VectorEmbeddingPolicyDefinition + { + public VectorEmbeddingPolicyDefinition(ContainerBuilder parent, Collection embeddings, Action attachCallback); + public ContainerBuilder Attach(); + } + public class VectorIndexDefinition + { + public VectorIndexDefinition(T parent, Action attachCallback); + public T Attach(); + public VectorIndexDefinition Path(string path, VectorIndexType indexType); + public VectorIndexDefinition WithIndexingSearchListSize(int indexingSearchListSize); + public VectorIndexDefinition WithQuantizationByteSize(int quantizationByteSize); + public VectorIndexDefinition WithVectorIndexShardKey(string[] vectorIndexShardKey); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static bool ArrayContainsAll(this IEnumerable obj, params object[] values); + public static bool ArrayContainsAny(this IEnumerable obj, params object[] values); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static int DocumentId(this object obj); + public static bool FullTextContains(this object obj, string search); + public static bool FullTextContainsAll(this object obj, params string[] searches); + public static bool FullTextContainsAny(this object obj, params string[] searches); + public static double FullTextScore(this TSource obj, params string[] terms); + public static string GetIndexMetrics(this Response response); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static IOrderedQueryable OrderByRank(this IQueryable source, Expression> scoreFunction); + public static bool RegexMatch(this object obj, string regularExpression); + public static bool RegexMatch(this object obj, string regularExpression, string searchModifier); + public static double RRF(params double[] scoringFunctions); + public static double RRF(double[] scoringFunctions, double[] weights); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query, IDictionary namedParameters); + public static FeedIterator ToStreamIterator(this IQueryable query); + public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, CosmosLinqExtensions.VectorDistanceOptions options); + public sealed class VectorDistanceOptions + { + public VectorDistanceOptions(); + public Nullable DataType { get; set; } + public Nullable DistanceFunction { get; set; } + public Nullable SearchListSizeMultiplier { get; set; } + } + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStoredProcedureStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionStreamAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureStreamAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerStreamAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionStreamAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + Upsert = (short)5, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/src/AssemblyInfo.cs b/Microsoft.Azure.Cosmos/src/AssemblyInfo.cs index c172fb7381..89c5a01ccb 100644 --- a/Microsoft.Azure.Cosmos/src/AssemblyInfo.cs +++ b/Microsoft.Azure.Cosmos/src/AssemblyInfo.cs @@ -2,7 +2,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using Microsoft.Azure.Cosmos.Direct; [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyKeys.MoqPublicKey)] @@ -35,11 +35,13 @@ [assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.Analytics.Core" + AssemblyKeys.ProductPublicKey)] [assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.Analytics.Core" + AssemblyKeys.TestPublicKey)] [assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle" + AssemblyKeys.ProductPublicKey)] -[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle" + AssemblyKeys.TestPublicKey)] +[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle" + AssemblyKeys.TestPublicKey)] [assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle.JobProcessor" + AssemblyKeys.ProductPublicKey)] -[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle.JobProcessor" + AssemblyKeys.TestPublicKey)] +[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle.JobProcessor" + AssemblyKeys.TestPublicKey)] +[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle.QueryTests" + AssemblyKeys.ProductPublicKey)] +[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle.QueryTests" + AssemblyKeys.TestPublicKey)] [assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle.UnitTests" + AssemblyKeys.ProductPublicKey)] -[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle.UnitTests" + AssemblyKeys.TestPublicKey)] +[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.QueryOracle.UnitTests" + AssemblyKeys.TestPublicKey)] [assembly: InternalsVisibleTo("CosmosCTL" + AssemblyKeys.ProductPublicKey)] [assembly: InternalsVisibleTo("CosmosCTL" + AssemblyKeys.TestPublicKey)] [assembly: InternalsVisibleTo("CosmosBenchmark" + AssemblyKeys.ProductPublicKey)] diff --git a/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProvider.cs b/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProvider.cs index b7bc7a4475..9839bf1039 100644 --- a/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProvider.cs +++ b/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProvider.cs @@ -52,6 +52,12 @@ public abstract ValueTask GetUserAuthorizationTokenAsync( AuthorizationTokenType tokenType, ITrace trace); + public abstract ValueTask AddInferenceAuthorizationHeaderAsync( + INameValueCollection headersCollection, + Uri requestAddress, + string verb, + AuthorizationTokenType tokenType); + public abstract void TraceUnauthorized( DocumentClientException dce, string authorizationToken, diff --git a/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderMasterKey.cs b/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderMasterKey.cs index 134640ba10..278be856eb 100644 --- a/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderMasterKey.cs +++ b/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderMasterKey.cs @@ -214,6 +214,11 @@ private void Dispose(bool disposing) this.authKeyHashFunction = null; } + public override ValueTask AddInferenceAuthorizationHeaderAsync(INameValueCollection headersCollection, Uri requestAddress, string verb, AuthorizationTokenType tokenType) + { + throw new NotImplementedException("AddInferenceAuthorizationHeaderAsync is only valid for AAD"); + } + // Use C# finalizer syntax for finalization code. // This finalizer will run only if the Dispose method does not get called. // It gives your base class the opportunity to finalize. diff --git a/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderResourceToken.cs b/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderResourceToken.cs index 182d4c5ba7..1697589e5d 100644 --- a/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderResourceToken.cs +++ b/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderResourceToken.cs @@ -92,6 +92,11 @@ private void Dispose(bool disposing) // Do nothing } + public override ValueTask AddInferenceAuthorizationHeaderAsync(INameValueCollection headersCollection, Uri requestAddress, string verb, AuthorizationTokenType tokenType) + { + throw new NotImplementedException("AddInferenceAuthorizationHeaderAsync is only valid for AAD"); + } + // Use C# finalizer syntax for finalization code. // This finalizer will run only if the Dispose method does not get called. // It gives your base class the opportunity to finalize. diff --git a/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderTokenCredential.cs b/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderTokenCredential.cs index dff20331f6..09e422bb8a 100644 --- a/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderTokenCredential.cs +++ b/Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderTokenCredential.cs @@ -15,14 +15,18 @@ namespace Microsoft.Azure.Cosmos internal sealed class AuthorizationTokenProviderTokenCredential : AuthorizationTokenProvider { + private const string InferenceTokenPrefix = "Bearer "; internal readonly TokenCredentialCache tokenCredentialCache; private bool isDisposed = false; + internal readonly TokenCredential tokenCredential; + public AuthorizationTokenProviderTokenCredential( TokenCredential tokenCredential, Uri accountEndpoint, TimeSpan? backgroundTokenCredentialRefreshInterval) { + this.tokenCredential = tokenCredential ?? throw new ArgumentNullException(nameof(tokenCredential)); this.tokenCredentialCache = new TokenCredentialCache( tokenCredential: tokenCredential, accountEndpoint: accountEndpoint, @@ -71,6 +75,21 @@ public override async ValueTask AddAuthorizationHeaderAsync( } } + public override async ValueTask AddInferenceAuthorizationHeaderAsync( + INameValueCollection headersCollection, + Uri requestAddress, + string verb, + AuthorizationTokenType tokenType) + { + using (Trace trace = Trace.GetRootTrace(nameof(GetUserAuthorizationTokenAsync), TraceComponent.Authorization, TraceLevel.Info)) + { + string token = await this.tokenCredentialCache.GetTokenAsync(trace); + + string inferenceToken = $"{InferenceTokenPrefix}{token}"; + headersCollection.Add(HttpConstants.HttpHeaders.Authorization, inferenceToken); + } + } + public override void TraceUnauthorized( DocumentClientException dce, string authorizationToken, diff --git a/Microsoft.Azure.Cosmos/src/Authorization/AzureKeyCredentialAuthorizationTokenProvider.cs b/Microsoft.Azure.Cosmos/src/Authorization/AzureKeyCredentialAuthorizationTokenProvider.cs index 602c419a6c..03a0bff2a4 100644 --- a/Microsoft.Azure.Cosmos/src/Authorization/AzureKeyCredentialAuthorizationTokenProvider.cs +++ b/Microsoft.Azure.Cosmos/src/Authorization/AzureKeyCredentialAuthorizationTokenProvider.cs @@ -125,5 +125,10 @@ private void CheckAndRefreshTokenProvider() } } } + + public override ValueTask AddInferenceAuthorizationHeaderAsync(INameValueCollection headersCollection, Uri requestAddress, string verb, AuthorizationTokenType tokenType) + { + throw new NotImplementedException("AddInferenceAuthorizationHeaderAsync is only valid for AAD"); + } } } diff --git a/Microsoft.Azure.Cosmos/src/Authorization/CosmosScopeProvider.cs b/Microsoft.Azure.Cosmos/src/Authorization/CosmosScopeProvider.cs new file mode 100644 index 0000000000..41a05e137f --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Authorization/CosmosScopeProvider.cs @@ -0,0 +1,56 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos.Authorization +{ + using System; + using global::Azure.Core; + + internal sealed class CosmosScopeProvider : IScopeProvider + { + private const string AadInvalidScopeErrorMessage = "AADSTS500011"; + private const string AadDefaultScope = "https://cosmos.azure.com/.default"; + private const string ScopeFormat = "https://{0}/.default"; + + private readonly string accountScope; + private readonly string overrideScope; + private string currentScope; + + public CosmosScopeProvider(Uri accountEndpoint) + { + this.overrideScope = ConfigurationManager.AADScopeOverrideValue(defaultValue: null); + this.accountScope = string.Format(ScopeFormat, accountEndpoint.Host); + this.currentScope = this.overrideScope ?? this.accountScope; + } + + public TokenRequestContext GetTokenRequestContext() + { + return new TokenRequestContext(new[] { this.currentScope }); + } + + public bool TryFallback(Exception exception) + { + // If override scope is set, never fallback + if (!string.IsNullOrEmpty(this.overrideScope)) + { + return false; + } + + // If already using fallback scope, do not fallback again + if (this.currentScope == CosmosScopeProvider.AadDefaultScope) + { + return false; + } + +#pragma warning disable CDX1003 // DontUseExceptionToString + if (exception.ToString().Contains(CosmosScopeProvider.AadInvalidScopeErrorMessage) == true) + { + this.currentScope = CosmosScopeProvider.AadDefaultScope; + return true; + } +#pragma warning restore CDX1003 // DontUseExceptionToString + + return false; + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Authorization/IScopeProvider.cs b/Microsoft.Azure.Cosmos/src/Authorization/IScopeProvider.cs new file mode 100644 index 0000000000..545270f9eb --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Authorization/IScopeProvider.cs @@ -0,0 +1,14 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos.Authorization +{ + using System; + using global::Azure.Core; + + internal interface IScopeProvider + { + TokenRequestContext GetTokenRequestContext(); + bool TryFallback(Exception ex); + } +} diff --git a/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs b/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs index e844dbdec8..efb10277d8 100644 --- a/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs +++ b/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs @@ -10,7 +10,8 @@ namespace Microsoft.Azure.Cosmos using System.Threading; using System.Threading.Tasks; using global::Azure; - using global::Azure.Core; + using global::Azure.Core; + using Microsoft.Azure.Cosmos.Authorization; using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Cosmos.Resource.CosmosExceptions; using Microsoft.Azure.Cosmos.Tracing; @@ -36,9 +37,7 @@ internal sealed class TokenCredentialCache : IDisposable // If the background refresh fails with less than a minute then just allow the request to hit the exception. public static readonly TimeSpan MinimumTimeBetweenBackgroundRefreshInterval = TimeSpan.FromMinutes(1); - private const string ScopeFormat = "https://{0}/.default"; - - private readonly TokenRequestContext tokenRequestContext; + private readonly IScopeProvider scopeProvider; private readonly TokenCredential tokenCredential; private readonly CancellationTokenSource cancellationTokenSource; private readonly CancellationToken cancellationToken; @@ -51,7 +50,7 @@ internal sealed class TokenCredentialCache : IDisposable private Task? currentRefreshOperation = null; private AccessToken? cachedAccessToken = null; private bool isBackgroundTaskRunning = false; - private bool isDisposed = false; + private bool isDisposed = false; internal TokenCredentialCache( TokenCredential tokenCredential, @@ -65,14 +64,7 @@ internal TokenCredentialCache( throw new ArgumentNullException(nameof(accountEndpoint)); } - string? scopeOverride = ConfigurationManager.AADScopeOverrideValue(defaultValue: null); - - this.tokenRequestContext = new TokenRequestContext(new string[] - { - !string.IsNullOrEmpty(scopeOverride) - ? scopeOverride - : string.Format(TokenCredentialCache.ScopeFormat, accountEndpoint.Host) - }); + this.scopeProvider = new Microsoft.Azure.Cosmos.Authorization.CosmosScopeProvider(accountEndpoint); if (backgroundTokenCredentialRefreshInterval.HasValue) { @@ -129,7 +121,7 @@ public void Dispose() } this.cancellationTokenSource.Cancel(); - this.cancellationTokenSource.Dispose(); + this.cancellationTokenSource.Dispose(); this.isDisposed = true; } @@ -171,11 +163,13 @@ private async Task GetNewTokenAsync( private async ValueTask RefreshCachedTokenWithRetryHelperAsync( ITrace trace) - { + { + Exception? lastException = null; + const int totalRetryCount = 2; + TokenRequestContext tokenRequestContext = default; + try { - Exception? lastException = null; - const int totalRetryCount = 2; for (int retry = 0; retry < totalRetryCount; retry++) { if (this.cancellationToken.IsCancellationRequested) @@ -190,11 +184,13 @@ private async ValueTask RefreshCachedTokenWithRetryHelperAsync( name: nameof(this.RefreshCachedTokenWithRetryHelperAsync), component: TraceComponent.Authorization, level: Tracing.TraceLevel.Info)) - { + { try - { + { + tokenRequestContext = this.scopeProvider.GetTokenRequestContext(); + this.cachedAccessToken = await this.tokenCredential.GetTokenAsync( - requestContext: this.tokenRequestContext, + requestContext: tokenRequestContext, cancellationToken: this.cancellationToken); if (!this.cachedAccessToken.HasValue) @@ -219,32 +215,15 @@ private async ValueTask RefreshCachedTokenWithRetryHelperAsync( return this.cachedAccessToken.Value; } - catch (RequestFailedException requestFailedException) - { - lastException = requestFailedException; - getTokenTrace.AddDatum( - $"RequestFailedException at {DateTime.UtcNow.ToString(CultureInfo.InvariantCulture)}", - requestFailedException.Message); - - DefaultTrace.TraceError($"TokenCredential.GetToken() failed with RequestFailedException. scope = {string.Join(";", this.tokenRequestContext.Scopes)}, retry = {retry}, Exception = {lastException.Message}"); - - // Don't retry on auth failures - if (requestFailedException.Status == (int)HttpStatusCode.Unauthorized || - requestFailedException.Status == (int)HttpStatusCode.Forbidden) - { - this.cachedAccessToken = default; - throw; - } - } catch (OperationCanceledException operationCancelled) { lastException = operationCancelled; getTokenTrace.AddDatum( $"OperationCanceledException at {DateTime.UtcNow.ToString(CultureInfo.InvariantCulture)}", - operationCancelled.Message); - - DefaultTrace.TraceError( - $"TokenCredential.GetTokenAsync() failed. scope = {string.Join(";", this.tokenRequestContext.Scopes)}, retry = {retry}, Exception = {lastException.Message}"); + operationCancelled.Message); + + DefaultTrace.TraceError( + $"TokenCredential.GetTokenAsync() failed. scope = {string.Join(";", tokenRequestContext.Scopes)}, retry = {retry}, Exception = {lastException.Message}"); throw CosmosExceptionFactory.CreateRequestTimeoutException( message: ClientResources.FailedToGetAadToken, @@ -255,15 +234,29 @@ private async ValueTask RefreshCachedTokenWithRetryHelperAsync( innerException: lastException, trace: getTokenTrace); } - catch (Exception exception) - { - lastException = exception; - getTokenTrace.AddDatum( - $"Exception at {DateTime.UtcNow.ToString(CultureInfo.InvariantCulture)}", - exception.Message); - - DefaultTrace.TraceError( - $"TokenCredential.GetTokenAsync() failed. scope = {string.Join(";", this.tokenRequestContext.Scopes)}, retry = {retry}, Exception = {lastException.Message}"); + catch (Exception exception) + { + lastException = exception; + getTokenTrace.AddDatum( + $"Exception at {DateTime.UtcNow.ToString(CultureInfo.InvariantCulture)}", + exception.Message); + + DefaultTrace.TraceError($"TokenCredential.GetToken() failed with RequestFailedException. scope = {string.Join(";", tokenRequestContext.Scopes)}, retry = {retry}, Exception = {lastException.Message}"); + + // Don't retry on auth failures + if (exception is RequestFailedException requestFailedException && + (requestFailedException.Status == (int)HttpStatusCode.Unauthorized || + requestFailedException.Status == (int)HttpStatusCode.Forbidden)) + { + this.cachedAccessToken = default; + throw; + } + bool didFallback = this.scopeProvider.TryFallback(exception); + + if (didFallback) + { + DefaultTrace.TraceInformation($"TokenCredential.GetTokenAsync() failed. scope = {string.Join(";", tokenRequestContext.Scopes)}, retry = {retry}, Exception = {lastException.Message}. Fallback attempted: {didFallback}"); + } } } } diff --git a/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncBatcher.cs b/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncBatcher.cs index 90b38a6d99..feb4374279 100644 --- a/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncBatcher.cs +++ b/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncBatcher.cs @@ -36,6 +36,7 @@ internal class BatchAsyncBatcher private readonly int maxBatchOperationCount; private readonly InterlockIncrementCheck interlockIncrementCheck = new InterlockIncrementCheck(); private readonly CosmosClientContext clientContext; + private readonly ItemRequestOptions options; private long currentSize = 0; private bool dispatched = false; private bool isClientEncrypted = false; @@ -49,7 +50,8 @@ public BatchAsyncBatcher( CosmosSerializerCore serializerCore, BatchAsyncBatcherExecuteDelegate executor, BatchAsyncBatcherRetryDelegate retrier, - CosmosClientContext clientContext) + CosmosClientContext clientContext, + ItemRequestOptions options = null) { if (maxBatchOperationCount < 1) { @@ -68,6 +70,7 @@ public BatchAsyncBatcher( this.maxBatchOperationCount = maxBatchOperationCount; this.serializerCore = serializerCore ?? throw new ArgumentNullException(nameof(serializerCore)); this.clientContext = clientContext; + this.options = options; } public virtual bool TryAdd(ItemBatchOperation operation) @@ -166,7 +169,7 @@ private async Task DispatchHelperAsync( { ValueStopwatch stopwatch = ValueStopwatch.StartNew(); - PartitionKeyRangeBatchExecutionResult result = await this.executor(serverRequest, trace, cancellationToken); + PartitionKeyRangeBatchExecutionResult result = await this.executor(serverRequest, trace, this.options, cancellationToken); int numThrottle = result.ServerResponse.Any(r => r.StatusCode == (System.Net.HttpStatusCode)StatusCodes.TooManyRequests) ? 1 : 0; partitionMetric.Add( @@ -245,6 +248,7 @@ internal virtual async Task BatchAsyncBatcherExecuteDelegate( PartitionKeyRangeServerBatchRequest request, ITrace trace, + ItemRequestOptions requestOptions, CancellationToken cancellationToken); /// diff --git a/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncContainerExecutor.cs b/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncContainerExecutor.cs index be166ed5a9..0305678862 100644 --- a/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncContainerExecutor.cs +++ b/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncContainerExecutor.cs @@ -87,7 +87,7 @@ public virtual async Task AddAsync( operation, trace, cancellationToken).ConfigureAwait(false); - BatchAsyncStreamer streamer = this.GetOrAddStreamerForPartitionKeyRange(resolvedPartitionKeyRangeId); + BatchAsyncStreamer streamer = this.GetOrAddStreamerForPartitionKeyRange(resolvedPartitionKeyRangeId, itemRequestOptions); ItemBatchOperationContext context = new ItemBatchOperationContext( resolvedPartitionKeyRangeId, @@ -260,6 +260,7 @@ private async Task ResolvePartitionKeyRangeIdAsync( private async Task ExecuteAsync( PartitionKeyRangeServerBatchRequest serverRequest, ITrace trace, + ItemRequestOptions itemRequestOptions, CancellationToken cancellationToken) { SemaphoreSlim limiter = this.GetOrAddLimiterForPartitionKeyRange(serverRequest.PartitionKeyRangeId); @@ -267,12 +268,17 @@ private async Task ExecuteAsync( { using (Stream serverRequestPayload = serverRequest.TransferBodyStream()) { + RequestOptions requestOptions = new RequestOptions(); + if (itemRequestOptions != null && itemRequestOptions.AvailabilityStrategy != null) + { + requestOptions.AvailabilityStrategy = itemRequestOptions.AvailabilityStrategy; + } Debug.Assert(serverRequestPayload != null, "Server request payload expected to be non-null"); ResponseMessage responseMessage = await this.cosmosClientContext.ProcessResourceOperationStreamAsync( this.cosmosContainer.LinkUri, ResourceType.Document, OperationType.Batch, - new RequestOptions(), + requestOptions, cosmosContainerCore: this.cosmosContainer, feedRange: null, streamPayload: serverRequestPayload, @@ -296,7 +302,7 @@ private async Task ExecuteAsync( } } - private BatchAsyncStreamer GetOrAddStreamerForPartitionKeyRange(string partitionKeyRangeId) + private BatchAsyncStreamer GetOrAddStreamerForPartitionKeyRange(string partitionKeyRangeId, ItemRequestOptions options = null) { if (this.streamersByPartitionKeyRange.TryGetValue(partitionKeyRangeId, out BatchAsyncStreamer streamer)) { @@ -312,7 +318,8 @@ private BatchAsyncStreamer GetOrAddStreamerForPartitionKeyRange(string partition this.cosmosClientContext.SerializerCore, this.ExecuteAsync, this.ReBatchAsync, - this.cosmosClientContext); + this.cosmosClientContext, + options); if (!this.streamersByPartitionKeyRange.TryAdd(partitionKeyRangeId, newStreamer)) { newStreamer.Dispose(); diff --git a/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncStreamer.cs b/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncStreamer.cs index 097c0cff4a..8ea0b4a09b 100644 --- a/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncStreamer.cs +++ b/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncStreamer.cs @@ -39,6 +39,7 @@ internal class BatchAsyncStreamer : IDisposable private readonly BatchPartitionMetric oldPartitionMetric; private readonly BatchPartitionMetric partitionMetric; private readonly CosmosClientContext clientContext; + private readonly ItemRequestOptions options; private volatile BatchAsyncBatcher currentBatcher; private TimerWheelTimer currentTimer; @@ -59,7 +60,8 @@ public BatchAsyncStreamer( CosmosSerializerCore serializerCore, BatchAsyncBatcherExecuteDelegate executor, BatchAsyncBatcherRetryDelegate retrier, - CosmosClientContext clientContext) + CosmosClientContext clientContext, + ItemRequestOptions options = null) { if (maxBatchOperationCount < 1) { @@ -103,6 +105,7 @@ public BatchAsyncStreamer( this.timerWheel = timerWheel; this.serializerCore = serializerCore; this.clientContext = clientContext; + this.options = options; this.currentBatcher = this.CreateBatchAsyncBatcher(); this.ResetTimer(); @@ -210,7 +213,7 @@ private BatchAsyncBatcher GetBatchToDispatchAndCreate() private BatchAsyncBatcher CreateBatchAsyncBatcher() { - return new BatchAsyncBatcher(this.maxBatchOperationCount, this.maxBatchByteSize, this.serializerCore, this.executor, this.retrier, this.clientContext); + return new BatchAsyncBatcher(this.maxBatchOperationCount, this.maxBatchByteSize, this.serializerCore, this.executor, this.retrier, this.clientContext, this.options); } private async Task RunCongestionControlAsync() diff --git a/Microsoft.Azure.Cosmos/src/Batch/TransactionalBatchItemRequestOptions.cs b/Microsoft.Azure.Cosmos/src/Batch/TransactionalBatchItemRequestOptions.cs index 1bd52554cf..cac80c8c21 100644 --- a/Microsoft.Azure.Cosmos/src/Batch/TransactionalBatchItemRequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/Batch/TransactionalBatchItemRequestOptions.cs @@ -47,7 +47,8 @@ internal static TransactionalBatchItemRequestOptions FromItemRequestOptions(Item IfNoneMatchEtag = itemRequestOptions.IfNoneMatchEtag, Properties = itemRequestOptions.Properties, EnableContentResponseOnWrite = itemRequestOptions.EnableContentResponseOnWrite, - IsEffectivePartitionKeyRouting = itemRequestOptions.IsEffectivePartitionKeyRouting + IsEffectivePartitionKeyRouting = itemRequestOptions.IsEffectivePartitionKeyRouting, + AvailabilityStrategy = itemRequestOptions.AvailabilityStrategy }; return batchItemRequestOptions; } diff --git a/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/FeedManagement/PartitionLoadBalancerCore.cs b/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/FeedManagement/PartitionLoadBalancerCore.cs index 1516d142dd..c3f7af2460 100644 --- a/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/FeedManagement/PartitionLoadBalancerCore.cs +++ b/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/FeedManagement/PartitionLoadBalancerCore.cs @@ -89,14 +89,14 @@ private async Task RunAsync() catch (Exception e) { Extensions.TraceException(e); - DefaultTrace.TraceError("Partition load balancer lease add/update iteration failed"); + DefaultTrace.TraceError("Failed to acquire lease: Id={0}, Owner={1}, Timestamp={2}", lease.Id, lease.Owner, lease.Timestamp); } } } catch (Exception e) { Extensions.TraceException(e); - DefaultTrace.TraceError("Partition load balancer iteration failed"); + DefaultTrace.TraceError("Partition load balancer iteration failed - LeaseAcquireInterval: {0}", this.leaseAcquireInterval); } await Task.Delay(this.leaseAcquireInterval, this.cancellationTokenSource.Token).ConfigureAwait(false); diff --git a/Microsoft.Azure.Cosmos/src/ClientRetryPolicy.cs b/Microsoft.Azure.Cosmos/src/ClientRetryPolicy.cs index 9d2d142394..3e81c62d0d 100644 --- a/Microsoft.Azure.Cosmos/src/ClientRetryPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/ClientRetryPolicy.cs @@ -27,8 +27,7 @@ internal sealed class ClientRetryPolicy : IDocumentClientRetryPolicy private readonly IDocumentClientRetryPolicy throttlingRetry; private readonly GlobalEndpointManager globalEndpointManager; private readonly GlobalPartitionEndpointManager partitionKeyRangeLocationCache; - private readonly bool enableEndpointDiscovery; - private readonly bool isPartitionLevelFailoverEnabled; + private readonly bool enableEndpointDiscovery; private readonly bool isThinClientEnabled; private int failoverRetryCount; @@ -39,14 +38,16 @@ internal sealed class ClientRetryPolicy : IDocumentClientRetryPolicy private bool isMultiMasterWriteRequest; private Uri locationEndpoint; private RetryContext retryContext; - private DocumentServiceRequest documentServiceRequest; + private DocumentServiceRequest documentServiceRequest; +#if !INTERNAL + private volatile bool addHubRegionProcessingOnlyHeader; +#endif public ClientRetryPolicy( GlobalEndpointManager globalEndpointManager, GlobalPartitionEndpointManager partitionKeyRangeLocationCache, RetryOptions retryOptions, - bool enableEndpointDiscovery, - bool isPartitionLevelFailoverEnabled, + bool enableEndpointDiscovery, bool isThinClientEnabled) { this.throttlingRetry = new ResourceThrottleRetryPolicy( @@ -60,8 +61,7 @@ public ClientRetryPolicy( this.sessionTokenRetryCount = 0; this.serviceUnavailableRetryCount = 0; this.canUseMultipleWriteLocations = false; - this.isMultiMasterWriteRequest = false; - this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; + this.isMultiMasterWriteRequest = false; this.isThinClientEnabled = isThinClientEnabled; } @@ -225,12 +225,18 @@ public void OnBeforeSendRequest(DocumentServiceRequest request) // set location-based routing directive based on request retry context request.RequestContext.RouteToLocation(this.retryContext.RetryLocationIndex, this.retryContext.RetryRequestOnPreferredLocations); } - } - + } +#if !INTERNAL + // If previous attempt failed with 404/1002, add the hub-region-processing-only header to all subsequent retry attempts + if (this.addHubRegionProcessingOnlyHeader) + { + request.Headers[HttpConstants.HttpHeaders.ShouldProcessOnlyInHubRegion] = bool.TrueString; + } +#endif // Resolve the endpoint for the request and pin the resolution to the resolved endpoint // This enables marking the endpoint unavailability on endpoint failover/unreachability this.locationEndpoint = this.isThinClientEnabled - && ThinClientStoreModel.IsOperationSupportedByThinClient(request) + && GatewayStoreModel.IsOperationSupportedByThinClient(request) ? this.globalEndpointManager.ResolveThinClientEndpoint(request) : this.globalEndpointManager.ResolveServiceEndpoint(request); @@ -321,15 +327,22 @@ private async Task ShouldRetryInternalAsync( markBothReadAndWriteAsUnavailable: false, forceRefresh: false, retryOnPreferredLocations: true); - } - - if (statusCode == HttpStatusCode.NotFound - && subStatusCode == SubStatusCodes.ReadSessionNotAvailable) + } + + if (statusCode == HttpStatusCode.NotFound && subStatusCode == SubStatusCodes.ReadSessionNotAvailable) { +#if !INTERNAL + // Only set the hub region processing header for single master accounts + // Set header only after the first retry attempt fails with 404/1002 + if (!this.canUseMultipleWriteLocations && this.sessionTokenRetryCount >= 1) + { + this.addHubRegionProcessingOnlyHeader = true; + } +#endif return this.ShouldRetryOnSessionNotAvailable(this.documentServiceRequest); - } - - // Received 503 due to client connect timeout or Gateway + } + + // Received 503 due to client connect timeout or Gateway if (statusCode == HttpStatusCode.ServiceUnavailable) { return this.TryMarkEndpointUnavailableForPkRangeAndRetryOnServiceUnavailable( @@ -500,7 +513,7 @@ private ShouldRetryResult ShouldRetryOnUnavailableEndpointStatusCodes() if (!this.canUseMultipleWriteLocations && !this.isReadRequest - && !this.isPartitionLevelFailoverEnabled) + && !this.partitionKeyRangeLocationCache.IsPartitionLevelAutomaticFailoverEnabled()) { // Write requests on single master cannot be retried if partition level failover is disabled. // This means there are no other regions available to serve the writes. diff --git a/Microsoft.Azure.Cosmos/src/CosmosClient.cs b/Microsoft.Azure.Cosmos/src/CosmosClient.cs index 05cbb846a0..5174362bb6 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosClient.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosClient.cs @@ -1162,6 +1162,20 @@ public virtual FeedIterator GetDatabaseQueryStreamIterator( this.ClientContext); } + /// + /// Creates a new instance of a distributed write transaction. + /// + /// An instance of Distributed transaction. +#if INTERNAL + public +#else + internal +#endif + virtual DistributedWriteTransaction CreateDistributedWriteTransaction() + { + return new DistributedWriteTransactionCore(this.ClientContext); + } + /// /// Send a request for creating a database. /// diff --git a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs index 4e2b9b4457..7ed8862878 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs @@ -174,7 +174,7 @@ public string ApplicationName /// /// During the CosmosClient initialization the account information, including the available regions, is obtained from the . /// The CosmosClient will use the value of to populate the preferred list with the account available regions that intersect with its value. - /// If the value of contains regions that are not an available region in the account, the values will be ignored. If the these invalid regions are added later to the account, the CosmosClient will use them if they are higher in the preference order. + /// If the value of contains regions that are not an available region in the account, the values will be ignored. If these invalid regions are added later to the account, the CosmosClient will use them if they are higher in the preference order. /// /// /// If during CosmosClient initialization, the is not reachable, the CosmosClient will attempt to recover and obtain the account information issuing requests to the regions in in the order that they are listed. @@ -466,7 +466,26 @@ public System.Text.Json.JsonSerializerOptions UseSystemTextJsonSerializerWithOpt /// The default value for this parameter is 'false'. /// internal bool EnableStreamPassThrough { get; set; } = false; - + + /// + /// Gets or sets a value indicating whether to use length-aware range comparators for EPK range comparisons. + /// Length-aware range comparators were introduced in Range class to handle EPK range comparisons correctly + /// in the case of a container's physical partition set consisting of fully and partially specified EPK values. + /// By default, length-aware range comparator is enabled. Refer to Range.cs in Msdata project for more details. + /// Range.LengthAwareMinComparer/LengthAwareMaxComparer. + /// Setting the value to false will disable length-aware range comparator and switch to using the regular + /// Range.MinComparer/MaxComparer. + /// + /// + /// The default value is true. + /// + internal bool UseLengthAwareRangeComparer { get; set; } = +#if !INTERNAL + true; +#else + false; +#endif + /// /// (Direct/TCP) Controls the amount of idle time after which unused connections are closed. /// @@ -1329,4 +1348,4 @@ public override bool CanConvert(Type objectType) } } } -} \ No newline at end of file +} diff --git a/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosArray.LazyCosmosArray.cs b/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosArray.LazyCosmosArray.cs index 3e70ccb483..0dffae22f5 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosArray.LazyCosmosArray.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosArray.LazyCosmosArray.cs @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.CosmosElements using System; using System.Collections.Generic; - using System.Linq; using Microsoft.Azure.Cosmos.Json; #if INTERNAL diff --git a/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosObject.EagerCosmosObject.cs b/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosObject.EagerCosmosObject.cs index f256ce41a7..7327590266 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosObject.EagerCosmosObject.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosObject.EagerCosmosObject.cs @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.CosmosElements using System; using System.Collections.Generic; - using System.Collections.Immutable; using System.Linq; using Microsoft.Azure.Cosmos.Json; diff --git a/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosUndefined.cs b/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosUndefined.cs index daceda2bc8..ba62c2cadd 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosUndefined.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosElements/CosmosUndefined.cs @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.CosmosElements using System; using Microsoft.Azure.Cosmos.Json; - using Microsoft.Azure.Cosmos.Query.Core.Monads; #if INTERNAL #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosTraceDiagnostics.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosTraceDiagnostics.cs index ef74ad3d6e..5eae8492ea 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosTraceDiagnostics.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosTraceDiagnostics.cs @@ -40,6 +40,11 @@ public CosmosTraceDiagnostics(ITrace trace) public override string ToString() { + if (this.Value is Tracing.Trace rootConcreteTrace) + { + rootConcreteTrace.SetWalkingStateRecursively(); + } + return this.ToJsonString(); } @@ -50,16 +55,31 @@ public override TimeSpan GetClientElapsedTime() public override IReadOnlyList<(string regionName, Uri uri)> GetContactedRegions() { + if (this.Value is Tracing.Trace rootConcreteTrace) + { + rootConcreteTrace.SetWalkingStateRecursively(); + } + return this.Value?.Summary?.RegionsContacted; } public override ServerSideCumulativeMetrics GetQueryMetrics() { + if (this.Value is Tracing.Trace rootConcreteTrace) + { + rootConcreteTrace.SetWalkingStateRecursively(); + } + return this.accumulatedMetrics.Value; } internal bool IsGoneExceptionHit() { + if (this.Value is Tracing.Trace rootConcreteTrace) + { + rootConcreteTrace.SetWalkingStateRecursively(); + } + return this.WalkTraceTreeForGoneException(this.Value); } diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransaction.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransaction.cs new file mode 100644 index 0000000000..806d2a1548 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransaction.cs @@ -0,0 +1,28 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Threading; + using System.Threading.Tasks; + + /// + /// Represents a distributed transaction that will be performed across partitions and/or collections. + /// +#if INTERNAL + public +#else + internal +#endif + abstract class DistributedTransaction + { + /// + /// Commits the distributed transaction. + /// + /// A to observe while waiting for the task to complete. + /// A containing a that represents the result of the transaction. + public abstract Task CommitTransactionAsync(CancellationToken cancellationToken); + } +} diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionCommitter.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionCommitter.cs new file mode 100644 index 0000000000..692af6840f --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionCommitter.cs @@ -0,0 +1,104 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Documents; + + internal class DistributedTransactionCommitter + { + // TODO: Move to HttpConstants.HttpHeaders once DTC headers are added centrally + private const string IdempotencyTokenHeader = "x-ms-dtc-operation-id"; + + private readonly IReadOnlyList operations; + private readonly CosmosClientContext clientContext; + + public DistributedTransactionCommitter( + IReadOnlyList operations, + CosmosClientContext clientContext) + { + this.operations = operations ?? throw new ArgumentNullException(nameof(operations)); + this.clientContext = clientContext ?? throw new ArgumentNullException(nameof(clientContext)); + } + + public async Task CommitTransactionAsync(CancellationToken cancellationToken) + { + try + { + cancellationToken.ThrowIfCancellationRequested(); + await DistributedTransactionCommitterUtils.ResolveCollectionRidsAsync( + this.operations, + this.clientContext, + cancellationToken); + + DistributedTransactionServerRequest serverRequest = await DistributedTransactionServerRequest.CreateAsync( + this.operations, + this.clientContext.SerializerCore, + cancellationToken); + + return await this.ExecuteCommitAsync(serverRequest, cancellationToken); + } + catch (Exception ex) + { + DefaultTrace.TraceError($"Distributed transaction failed: {ex.Message}"); + // await this.AbortTransactionAsync(cancellationToken); + throw; + } + } + + private async Task ExecuteCommitAsync( + DistributedTransactionServerRequest serverRequest, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + using (ITrace trace = Trace.GetRootTrace("Execute Distributed Transaction Commit", TraceComponent.Batch, TraceLevel.Info)) + { + using (MemoryStream bodyStream = serverRequest.TransferBodyStream()) + { + ResponseMessage responseMessage = await this.clientContext.ProcessResourceOperationStreamAsync( + resourceUri: "/dtc/ops", + resourceType: ResourceType.Document, // TODO: Update to a new ResourceType specific to DTC + operationType: OperationType.Batch, // TODO: Update to a new OperationType specific to DTC + requestOptions: null, + cosmosContainerCore: null, + partitionKey: null, + itemId: null, + streamPayload: bodyStream, + requestEnricher: requestMessage => this.EnrichRequestMessage(requestMessage, serverRequest), + trace: trace, + cancellationToken: cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + return await DistributedTransactionResponse.FromResponseMessageAsync( + responseMessage, + serverRequest, + this.clientContext.SerializerCore, + serverRequest.IdempotencyToken, + trace, + cancellationToken); + } + } + } + + private void EnrichRequestMessage(RequestMessage requestMessage, DistributedTransactionServerRequest serverRequest) + { + // Set DTC-specific headers + requestMessage.Headers.Add(IdempotencyTokenHeader, serverRequest.IdempotencyToken.ToString()); + requestMessage.UseGatewayMode = true; + } + + private Task AbortTransactionAsync(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionCommitterUtils.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionCommitterUtils.cs new file mode 100644 index 0000000000..8647dd904e --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionCommitterUtils.cs @@ -0,0 +1,47 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Documents; + + internal class DistributedTransactionCommitterUtils + { + public static async Task ResolveCollectionRidsAsync( + IReadOnlyList operations, + CosmosClientContext clientContext, + CancellationToken cancellationToken) + { + IEnumerable> groupedOperations = operations + .GroupBy(op => $"/dbs/{op.Database}/colls/{op.Container}"); + + foreach (IGrouping group in groupedOperations) + { + cancellationToken.ThrowIfCancellationRequested(); + + string collectionPath = group.Key; + ContainerProperties containerProperties = await clientContext.GetCachedContainerPropertiesAsync( + collectionPath, + NoOpTrace.Singleton, + cancellationToken); + + string containerResourceId = containerProperties.ResourceId; + ResourceId resourceId = ResourceId.Parse(containerResourceId); + string databaseResourceId = resourceId.DatabaseId.ToString(); + + foreach (DistributedTransactionOperation operation in group) + { + operation.CollectionResourceId = containerResourceId; + operation.DatabaseResourceId = databaseResourceId; + } + } + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionOperation.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionOperation.cs new file mode 100644 index 0000000000..6ad3351298 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionOperation.cs @@ -0,0 +1,115 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.IO; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Documents; + + /// + /// Represents an operation on a document which will be executed as a part of a distributed transaction. + /// + internal class DistributedTransactionOperation + { + protected Memory body; + + public DistributedTransactionOperation( + OperationType operationType, + int operationIndex, + string database, + string container, + PartitionKey partitionKey, + string id = null) + { + this.OperationType = operationType; + this.OperationIndex = operationIndex; + this.PartitionKey = partitionKey; + this.Database = database; + this.Container = container; + this.Id = id; + } + + public PartitionKey PartitionKey { get; internal set; } + + public string Database { get; internal set; } + + public string Container { get; internal set; } + + public OperationType OperationType { get; internal set; } + + public int OperationIndex { get; internal set; } + + public string Id { get; internal set; } + + public string CollectionResourceId { get; internal set; } + + public string DatabaseResourceId { get; internal set; } + + internal string PartitionKeyJson { get; set; } + + internal string SessionToken { get; set; } + + internal string ETag { get; set; } + + internal Stream ResourceStream { get; set; } + + internal Memory ResourceBody + { + get => this.body; + set => this.body = value; + } + + internal virtual async Task MaterializeResourceAsync(CosmosSerializerCore serializerCore, CancellationToken cancellationToken) + { + if (this.body.IsEmpty && this.ResourceStream != null) + { + this.body = await BatchExecUtils.StreamToMemoryAsync(this.ResourceStream, cancellationToken); + } + } + } + + internal class DistributedTransactionOperation : DistributedTransactionOperation + { + public DistributedTransactionOperation( + Documents.OperationType operationType, + int operationIndex, + string database, + string container, + PartitionKey partitionKey, + T resource) + : base(operationType, operationIndex, database, container, partitionKey) + { + this.Resource = resource; + } + + public DistributedTransactionOperation( + Documents.OperationType operationType, + int operationIndex, + string database, + string container, + PartitionKey partitionKey, + string id, + T resource) + : base(operationType, operationIndex, database, container, partitionKey, id) + { + this.Resource = resource; + } + + public T Resource { get; internal set; } + + internal override Task MaterializeResourceAsync(CosmosSerializerCore serializerCore, CancellationToken cancellationToken) + { + if (this.body.IsEmpty && this.Resource != null) + { + this.ResourceStream = serializerCore.ToStream(this.Resource); + return base.MaterializeResourceAsync(serializerCore, cancellationToken); + } + + return Task.CompletedTask; + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionOperationResult.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionOperationResult.cs new file mode 100644 index 0000000000..3618c55a86 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionOperationResult.cs @@ -0,0 +1,140 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.IO; + using System.Net; + using System.Text.Json; + using System.Text.Json.Serialization; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Documents; + + /// + /// Represents result of a specific operation in distributed transaction. + /// +#if INTERNAL + public +#else + internal +#endif + class DistributedTransactionOperationResult + { + internal DistributedTransactionOperationResult(HttpStatusCode statusCode) + { + this.StatusCode = statusCode; + } + + internal DistributedTransactionOperationResult(DistributedTransactionOperationResult other) + { + this.Index = other.Index; + this.StatusCode = other.StatusCode; + this.SubStatusCode = other.SubStatusCode; + this.ETag = other.ETag; + this.ResourceStream = other.ResourceStream; + this.SessionToken = other.SessionToken; + this.RequestCharge = other.RequestCharge; + this.ActivityId = other.ActivityId; + this.Trace = other.Trace; + } + + /// + /// Initializes a new instance of the class. + /// This protected constructor is intended for use by derived classes. + /// + [JsonConstructor] + protected DistributedTransactionOperationResult() + { + } + + /// + /// Gets the index of this operation within the distributed transaction. + /// + [JsonInclude] + [JsonPropertyName("index")] + public virtual int Index { get; internal set; } + + /// + /// Gets the HTTP status code returned by the operation. + /// + [JsonInclude] + [JsonPropertyName("statuscode")] + public virtual HttpStatusCode StatusCode { get; internal set; } + + /// + /// Gets a value indicating whether the HTTP status code returned by the operation indicates success. + /// + [JsonIgnore] + public virtual bool IsSuccessStatusCode => (int)this.StatusCode >= 200 && (int)this.StatusCode <= 299; + + /// + /// Gets the entity tag (ETag) associated with the operation result. + /// The ETag is used for concurrency control and represents the version of the resource. + /// + [JsonInclude] + [JsonPropertyName("etag")] + public virtual string ETag { get; internal set; } + + /// + /// Gets the session token associated with the operation result. + /// + [JsonInclude] + [JsonPropertyName("sessionToken")] + public virtual string SessionToken { get; internal set; } + + /// + /// Gets the resource stream associated with the operation result. + /// The stream contains the raw response payload returned by the operation. + /// + [JsonIgnore] + public virtual Stream ResourceStream { get; internal set; } + + /// + /// Used for JSON deserialization of the base64-encoded resource body. + /// + [JsonInclude] + [JsonPropertyName("resourcebody")] + internal string ResourceBodyBase64 + { + get => null; // Write-only for deserialization + set + { + if (!string.IsNullOrEmpty(value)) + { + byte[] resourceBody = Convert.FromBase64String(value); + this.ResourceStream = new MemoryStream(resourceBody, 0, resourceBody.Length, writable: false, publiclyVisible: true); + } + } + } + + /// + /// Request charge in request units for the operation. + /// + [JsonPropertyName("requestCharge")] + internal virtual double RequestCharge { get; set; } + + [JsonPropertyName("substatuscode")] + internal virtual SubStatusCodes SubStatusCode { get; set; } + + /// + /// ActivityId related to the operation. + /// + [JsonIgnore] + internal virtual string ActivityId { get; set; } + + [JsonIgnore] + internal ITrace Trace { get; set; } + + /// + /// Creates a from a JSON element. + /// + /// The JSON element containing the operation result. + /// The deserialized operation result. + internal static DistributedTransactionOperationResult FromJson(JsonElement json) + { + return JsonSerializer.Deserialize(json); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionRequest.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionRequest.cs new file mode 100644 index 0000000000..3b923be8f9 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionRequest.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using Microsoft.Azure.Documents; + + internal class DistributedTransactionRequest + { + public DistributedTransactionRequest( + IReadOnlyList operations, + OperationType operationType = OperationType.Batch, + ResourceType resourceType = ResourceType.Document) + { + this.Operations = operations ?? throw new ArgumentNullException(nameof(operations)); + this.IdempotencyToken = Guid.NewGuid(); + this.OperationType = operationType; + this.ResourceType = resourceType; + } + + public Guid IdempotencyToken { get; set; } + + public OperationType OperationType { get; set; } + + public ResourceType ResourceType { get; set; } + + public IReadOnlyList Operations { get; set; } + } +} diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionResponse.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionResponse.cs new file mode 100644 index 0000000000..490c782d9b --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionResponse.cs @@ -0,0 +1,382 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Text.Json; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Documents; + + /// + /// Represents the response for a distributed transaction operation. + /// +#if INTERNAL + public +#else + internal +#endif + class DistributedTransactionResponse : IReadOnlyList, IDisposable + { + private const string IdempotencyTokenHeader = "x-ms-dtc-operation-id"; + + private List results; + private bool isDisposed; + + private DistributedTransactionResponse( + HttpStatusCode statusCode, + SubStatusCodes subStatusCode, + string errorMessage, + Headers headers, + IReadOnlyList operations, + CosmosSerializerCore serializer, + ITrace trace, + Guid idempotencyToken, + string serverDiagnostics = null) + { + this.Headers = headers; + this.StatusCode = statusCode; + this.SubStatusCode = subStatusCode; + this.ErrorMessage = errorMessage; + this.Operations = operations; + this.SerializerCore = serializer; + this.Trace = trace; + this.IdempotencyToken = idempotencyToken; + this.ServerDiagnostics = serverDiagnostics; + } + + /// + /// Initializes a new instance of the class. + /// + protected DistributedTransactionResponse() + { + } + + /// + /// Gets the at the specified index in the response. + /// + /// The zero-based index of the operation result to get. + /// The at the specified index. + public virtual DistributedTransactionOperationResult this[int index] + { + get + { + this.ThrowIfDisposed(); + + if (this.results == null || index < 0 || index >= this.results.Count) + { + throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range."); + } + + return this.results[index]; + } + } + + /// + /// Gets the headers associated with the distributed transaction response. + /// + public virtual Headers Headers { get; } + + /// + /// Gets the ActivityId that identifies the server request made to execute the transaction. + /// + public virtual string ActivityId => this.Headers?.ActivityId; + + /// + /// Gets the request charge for the distributed transaction request. + /// + public virtual double RequestCharge => this.Headers?.RequestCharge ?? 0; + + /// + /// Gets the HTTP status code for the distributed transaction response. + /// + public virtual HttpStatusCode StatusCode { get; } + + /// + /// Gets a value indicating whether the transaction was processed successfully. + /// + public virtual bool IsSuccessStatusCode => (int)this.StatusCode >= 200 && (int)this.StatusCode <= 299; + + /// + /// Gets the error message associated with the distributed transaction response, if any. + /// + public virtual string ErrorMessage { get; } + + /// + /// Gets the number of operation results in the distributed transaction response. + /// + public virtual int Count => this.results?.Count ?? 0; + + /// + /// Gets the idempotency token associated with this distributed transaction. + /// + public virtual Guid IdempotencyToken { get; } + + /// + /// Gets the server-side diagnostic information for the transaction. + /// + public virtual string ServerDiagnostics { get; } + + internal virtual SubStatusCodes SubStatusCode { get; } + + internal virtual CosmosSerializerCore SerializerCore { get; } + + internal IReadOnlyList Operations { get; } + + internal ITrace Trace { get; } + + /// + /// Returns an enumerator that iterates through the operation results. + /// + /// An enumerator for the operation results. + public virtual IEnumerator GetEnumerator() + { + return this.results?.GetEnumerator() + ?? ((IList)Array.Empty()).GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the operation results. + /// + /// An enumerator for the operation results. + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + /// + /// Releases the unmanaged resources used by the and optionally releases the managed resources. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Creates a from a . + /// + internal static async Task FromResponseMessageAsync( + ResponseMessage responseMessage, + DistributedTransactionServerRequest serverRequest, + CosmosSerializerCore serializer, + Guid requestIdempotencyToken, + ITrace trace, + CancellationToken cancellationToken) + { + using (ITrace createResponseTrace = trace.StartChild("Create Distributed Transaction Response", TraceComponent.Batch, TraceLevel.Info)) + { + cancellationToken.ThrowIfCancellationRequested(); + + // Extract idempotency token from response headers, fallback to request token if not present + Guid idempotencyToken = GetIdempotencyTokenFromHeaders(responseMessage.Headers, requestIdempotencyToken); + + DistributedTransactionResponse response = null; + MemoryStream memoryStream = null; + + try + { + if (responseMessage.Content != null) + { + Stream content = responseMessage.Content; + + // Ensure the stream is seekable + if (!content.CanSeek) + { + memoryStream = new MemoryStream(); + await responseMessage.Content.CopyToAsync(memoryStream); + memoryStream.Position = 0; + content = memoryStream; + } + + response = await PopulateFromJsonContentAsync( + content, + responseMessage, + serverRequest, + serializer, + idempotencyToken, + createResponseTrace, + cancellationToken); + } + + // If we couldn't parse JSON content or there was no content, create default response + response ??= new DistributedTransactionResponse( + responseMessage.StatusCode, + responseMessage.Headers.SubStatusCode, + responseMessage.ErrorMessage, + responseMessage.Headers, + serverRequest.Operations, + serializer, + createResponseTrace, + idempotencyToken); + + // Validate results count matches operations count + if (response.results == null || response.results.Count != serverRequest.Operations.Count) + { + if (responseMessage.IsSuccessStatusCode) + { + // Server should guarantee results count equals operations count on success + return new DistributedTransactionResponse( + HttpStatusCode.InternalServerError, + SubStatusCodes.Unknown, + ClientResources.InvalidServerResponse, + responseMessage.Headers, + serverRequest.Operations, + serializer, + createResponseTrace, + idempotencyToken); + } + + response.CreateAndPopulateResults(serverRequest.Operations, createResponseTrace); + } + + return response; + } + finally + { + memoryStream?.Dispose(); + } + } + } + + /// + /// Disposes the disposable members held by this class. + /// + /// True to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + if (disposing && this.results != null) + { + foreach (DistributedTransactionOperationResult result in this.results) + { + result.ResourceStream?.Dispose(); + } + } + + this.results = null; + this.isDisposed = true; + } + + private static Guid GetIdempotencyTokenFromHeaders(Headers headers, Guid fallbackToken) + { + if (headers != null && + headers.TryGetValue(IdempotencyTokenHeader, out string tokenValue) && + Guid.TryParse(tokenValue, out Guid idempotencyToken)) + { + return idempotencyToken; + } + + return fallbackToken; + } + + private void ThrowIfDisposed() + { + if (this.isDisposed) + { + throw new ObjectDisposedException(nameof(DistributedTransactionResponse)); + } + } + + private static async Task PopulateFromJsonContentAsync( + Stream content, + ResponseMessage responseMessage, + DistributedTransactionServerRequest serverRequest, + CosmosSerializerCore serializer, + Guid idempotencyToken, + ITrace trace, + CancellationToken cancellationToken) + { + List results = new List(); + + try + { + using (JsonDocument responseJson = await JsonDocument.ParseAsync(content, cancellationToken: cancellationToken)) + { + JsonElement root = responseJson.RootElement; + + // Parse operation results from "operationResponses" array + if (root.TryGetProperty("operationResponses", out JsonElement operationResponses) && + operationResponses.ValueKind == JsonValueKind.Array) + { + foreach (JsonElement operationElement in operationResponses.EnumerateArray()) + { + cancellationToken.ThrowIfCancellationRequested(); + + DistributedTransactionOperationResult operationResult = DistributedTransactionOperationResult.FromJson(operationElement); + operationResult.Trace = trace; + operationResult.SessionToken ??= responseMessage.Headers.Session; + operationResult.ActivityId = responseMessage.Headers.ActivityId; + results.Add(operationResult); + } + } + } + } + catch (JsonException) + { + // If JSON parsing fails, return null to fall back to default response + return null; + } + + HttpStatusCode finalStatusCode = responseMessage.StatusCode; + SubStatusCodes finalSubStatusCode = responseMessage.Headers.SubStatusCode; + + // Promote operation error status for MultiStatus responses + if ((int)finalStatusCode == (int)StatusCodes.MultiStatus) + { + foreach (DistributedTransactionOperationResult result in results) + { + if ((int)result.StatusCode != (int)StatusCodes.FailedDependency && + (int)result.StatusCode >= (int)StatusCodes.StartingErrorCode) + { + finalStatusCode = result.StatusCode; + finalSubStatusCode = result.SubStatusCode; + break; + } + } + } + + return new DistributedTransactionResponse( + finalStatusCode, + finalSubStatusCode, + responseMessage.ErrorMessage, + responseMessage.Headers, + serverRequest.Operations, + serializer, + trace, + idempotencyToken) + { + results = results + }; + } + + private void CreateAndPopulateResults( + IReadOnlyList operations, + ITrace trace) + { + this.results = new List(operations.Count); + + for (int i = 0; i < operations.Count; i++) + { + this.results.Add(new DistributedTransactionOperationResult(this.StatusCode) + { + SubStatusCode = this.SubStatusCode, + SessionToken = this.Headers?.Session, + ActivityId = this.ActivityId, + Trace = trace + }); + } + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionSerializer.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionSerializer.cs new file mode 100644 index 0000000000..2cbe7f5d1b --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionSerializer.cs @@ -0,0 +1,124 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Text.Json; + using Microsoft.Azure.Documents; + + /// + /// Serializes distributed transaction requests to JSON format. + /// + internal static class DistributedTransactionSerializer + { + /// + /// Serializes a distributed transaction request body to a JSON stream. + /// The body contains only the operations array. Other metadata like idempotencyToken, + /// operationType, and resourceType are sent as HTTP headers per the spec. + /// + /// The list of operations to include in the request. + /// A MemoryStream containing the JSON-serialized request body. + public static MemoryStream SerializeRequest(IReadOnlyList operations) + { + MemoryStream stream = new MemoryStream(); + + using (Utf8JsonWriter jsonWriter = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = false })) + { + jsonWriter.WriteStartObject(); + + // operations array + jsonWriter.WriteStartArray("operations"); + + foreach (DistributedTransactionOperation operation in operations) + { + WriteOperation(jsonWriter, operation); + } + + jsonWriter.WriteEndArray(); + + jsonWriter.WriteEndObject(); + jsonWriter.Flush(); + } + + stream.Position = 0; + return stream; + } + + /// + /// Writes a single operation to the JSON writer. + /// Keys match the C++ DistributedTransactionOperation model. + /// + private static void WriteOperation(Utf8JsonWriter jsonWriter, DistributedTransactionOperation operation) + { + jsonWriter.WriteStartObject(); + + // databaseName + jsonWriter.WriteString("databaseName", operation.Database); + + // collectionName + jsonWriter.WriteString("collectionName", operation.Container); + + // collectionResourceId + if (operation.CollectionResourceId != null) + { + jsonWriter.WriteString("collectionResourceId", operation.CollectionResourceId); + } + + // databaseResourceId + if (operation.DatabaseResourceId != null) + { + jsonWriter.WriteString("databaseResourceId", operation.DatabaseResourceId); + } + + // id + if (operation.Id != null) + { + jsonWriter.WriteString("id", operation.Id); + } + + // partitionKey + if (operation.PartitionKeyJson != null) + { + jsonWriter.WriteString("partitionKey", operation.PartitionKeyJson); + } + + // index (uint32) + if (operation.OperationIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(operation.OperationIndex), "Operation index must be non-negative."); + } + jsonWriter.WriteNumber("index", (uint)operation.OperationIndex); + + //resourceBody - written as nested JSON object + if (!operation.ResourceBody.IsEmpty) + { + jsonWriter.WritePropertyName("resourceBody"); + jsonWriter.WriteRawValue(operation.ResourceBody.Span, skipInputValidation: true); + } + + // sessionToken + if (operation.SessionToken != null) + { + jsonWriter.WriteString("sessionToken", operation.SessionToken); + } + + // etag + if (operation.ETag != null) + { + jsonWriter.WriteString("etag", operation.ETag); + } + + // operationType (uint16) + jsonWriter.WriteNumber("operationType", (ushort)operation.OperationType); + + // resourceType (uint16) + jsonWriter.WriteNumber("resourceType", (ushort)ResourceType.Document); + + jsonWriter.WriteEndObject(); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionServerRequest.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionServerRequest.cs new file mode 100644 index 0000000000..6cbab1e5f8 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionServerRequest.cs @@ -0,0 +1,59 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Threading; + using System.Threading.Tasks; + + internal class DistributedTransactionServerRequest + { + private readonly CosmosSerializerCore serializerCore; + private MemoryStream bodyStream; + + private DistributedTransactionServerRequest( + IReadOnlyList operations, + CosmosSerializerCore serializerCore) + { + this.Operations = operations ?? throw new ArgumentNullException(nameof(operations)); + this.serializerCore = serializerCore ?? throw new ArgumentNullException(nameof(serializerCore)); + this.IdempotencyToken = Guid.NewGuid(); + } + + public IReadOnlyList Operations { get; } + + public Guid IdempotencyToken { get; private set; } + + public static async Task CreateAsync( + IReadOnlyList operations, + CosmosSerializerCore serializerCore, + CancellationToken cancellationToken) + { + DistributedTransactionServerRequest request = new DistributedTransactionServerRequest(operations, serializerCore); + await request.CreateBodyStreamAsync(cancellationToken); + return request; + } + + public MemoryStream TransferBodyStream() + { + MemoryStream bodyStream = this.bodyStream; + this.bodyStream = null; + return bodyStream; + } + + private async Task CreateBodyStreamAsync(CancellationToken cancellationToken) + { + foreach (DistributedTransactionOperation operation in this.Operations) + { + await operation.MaterializeResourceAsync(this.serializerCore, cancellationToken); + operation.PartitionKeyJson ??= operation.PartitionKey.ToJsonString(); + } + + this.bodyStream = DistributedTransactionSerializer.SerializeRequest(this.Operations); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedWriteTransaction.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedWriteTransaction.cs new file mode 100644 index 0000000000..015db8e57a --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedWriteTransaction.cs @@ -0,0 +1,97 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System.Collections.Generic; + + /// + /// Represents a distributed transaction that supports write operations across multiple partitions and containers. + /// +#if INTERNAL + public +#else + internal +#endif + abstract class DistributedWriteTransaction : DistributedTransaction + { + /// + /// Adds a create operation to the distributed transaction. + /// + /// The type of the resource to create. + /// The name of the database containing the container. + /// The name of the container where the item will be created. + /// The partition key for the item. + /// The resource to create. + /// The current instance for method chaining. + public abstract DistributedWriteTransaction CreateItem( + string database, + string collection, + PartitionKey partitionKey, + T resource); + + /// + /// Adds a replace operation to the distributed transaction. + /// + /// The type of the resource to replace. + /// The name of the database containing the container. + /// The name of the container where the item exists. + /// The partition key for the item. + /// The unique identifier of the item to replace. + /// The resource with updated values. + /// The current instance for method chaining. + public abstract DistributedWriteTransaction ReplaceItem( + string database, + string collection, + PartitionKey partitionKey, + string id, + T resource); + + /// + /// Adds a delete operation to the distributed transaction. + /// + /// The name of the database containing the container. + /// The name of the container where the item exists. + /// The partition key for the item. + /// The unique identifier of the item to delete. + /// The current instance for method chaining. + public abstract DistributedWriteTransaction DeleteItem( + string database, + string collection, + PartitionKey partitionKey, + string id); + + /// + /// Adds a patch operation to the distributed transaction. + /// + /// The name of the database containing the container. + /// The name of the container where the item exists. + /// The partition key for the item. + /// The unique identifier of the item to patch. + /// The list of to apply to the item. + /// The current instance for method chaining. + public abstract DistributedWriteTransaction PatchItem( + string database, + string collection, + PartitionKey partitionKey, + string id, + IReadOnlyList patchOperations); + + /// + /// Adds an upsert operation to the distributed transaction. + /// If the item exists, it will be replaced; otherwise, it will be created. + /// + /// The type of the resource to upsert. + /// The name of the database containing the container. + /// The name of the container where the item will be upserted. + /// The partition key for the item. + /// The resource to upsert. + /// The current instance for method chaining. + public abstract DistributedWriteTransaction UpsertItem( + string database, + string collection, + PartitionKey partitionKey, + T resource); + } +} diff --git a/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedWriteTransactionCore.cs b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedWriteTransactionCore.cs new file mode 100644 index 0000000000..c2b39a8c1b --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedWriteTransactionCore.cs @@ -0,0 +1,157 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Documents; + + internal class DistributedWriteTransactionCore : DistributedWriteTransaction + { + private readonly CosmosClientContext clientContext; + private readonly List operations; + + internal DistributedWriteTransactionCore(CosmosClientContext clientContext) + { + this.clientContext = clientContext ?? throw new ArgumentNullException(nameof(clientContext)); + this.operations = new List(); + } + + public override DistributedWriteTransaction CreateItem(string database, string collection, PartitionKey partitionKey, T resource) + { + DistributedWriteTransactionCore.ValidateContainerReference(database, collection); + DistributedWriteTransactionCore.ValidateResource(resource); + + this.operations.Add( + new DistributedTransactionOperation( + operationType: OperationType.Create, + operationIndex: this.operations.Count, + database, + collection, + partitionKey, + resource)); + return this; + } + + public override DistributedWriteTransaction ReplaceItem(string database, string collection, PartitionKey partitionKey, string id, T resource) + { + DistributedWriteTransactionCore.ValidateContainerReference(database, collection); + DistributedWriteTransactionCore.ValidateItemId(id); + DistributedWriteTransactionCore.ValidateResource(resource); + + this.operations.Add( + new DistributedTransactionOperation( + operationType: OperationType.Replace, + operationIndex: this.operations.Count, + database, + collection, + partitionKey, + id, + resource)); + return this; + } + + public override DistributedWriteTransaction DeleteItem(string database, string collection, PartitionKey partitionKey, string id) + { + DistributedWriteTransactionCore.ValidateContainerReference(database, collection); + DistributedWriteTransactionCore.ValidateItemId(id); + + this.operations.Add( + new DistributedTransactionOperation( + operationType: OperationType.Delete, + operationIndex: this.operations.Count, + database, + collection, + partitionKey, + id: id)); + return this; + } + + public override DistributedWriteTransaction PatchItem( + string database, + string collection, + PartitionKey partitionKey, + string id, + IReadOnlyList patchOperations) + { + DistributedWriteTransactionCore.ValidateContainerReference(database, collection); + DistributedWriteTransactionCore.ValidateItemId(id); + + if (patchOperations == null || !patchOperations.Any()) + { + throw new ArgumentNullException(nameof(patchOperations)); + } + + PatchSpec patchSpec = new PatchSpec(patchOperations, new PatchItemRequestOptions()); + + this.operations.Add( + new DistributedTransactionOperation( + operationType: OperationType.Patch, + operationIndex: this.operations.Count, + database, + collection, + partitionKey, + resource: patchSpec)); + return this; + } + + public override DistributedWriteTransaction UpsertItem(string database, string collection, PartitionKey partitionKey, T resource) + { + DistributedWriteTransactionCore.ValidateContainerReference(database, collection); + DistributedWriteTransactionCore.ValidateResource(resource); + + this.operations.Add( + new DistributedTransactionOperation( + operationType: OperationType.Upsert, + operationIndex: this.operations.Count, + database, + collection, + partitionKey, + resource)); + return this; + } + + public override async Task CommitTransactionAsync(CancellationToken cancellationToken) + { + DistributedTransactionCommitter committer = new DistributedTransactionCommitter( + operations: this.operations, + clientContext: this.clientContext); + + return await committer.CommitTransactionAsync(cancellationToken); + } + + private static void ValidateContainerReference(string database, string collection) + { + if (string.IsNullOrWhiteSpace(database)) + { + throw new ArgumentNullException(nameof(database)); + } + + if (string.IsNullOrWhiteSpace(collection)) + { + throw new ArgumentNullException(nameof(collection)); + } + } + + private static void ValidateItemId(string id) + { + if (string.IsNullOrWhiteSpace(id)) + { + throw new ArgumentNullException(nameof(id)); + } + } + + private static void ValidateResource(T resource) + { + if (resource == null) + { + throw new ArgumentNullException(nameof(resource)); + } + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index c5fe1350f3..e4e4e9d68b 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -112,12 +112,12 @@ internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider private const int DefaultRntbdSendHangDetectionTimeSeconds = 10; private const bool DefaultEnableCpuMonitor = true; private const string DefaultInitTaskKey = "InitTaskKey"; - + /// /// Default thresholds for PPAF request hedging. - /// - private const int DefaultHedgingThresholdInMilliseconds = 1000; - private const int DefaultHedgingThresholdStepInMilliseconds = 500; + /// + internal const int DefaultHedgingThresholdInMilliseconds = 1000; + internal const int DefaultHedgingThresholdStepInMilliseconds = 500; private static readonly char[] resourceIdOrFullNameSeparators = new char[] { '/' }; private static readonly char[] resourceIdSeparators = new char[] { '/', '\\', '?', '#' }; @@ -133,9 +133,10 @@ internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider private bool isChaosInterceptorInititalized = false; //Auth - internal readonly AuthorizationTokenProvider cosmosAuthorization; - - private bool isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); + internal readonly AuthorizationTokenProvider cosmosAuthorization; + + private readonly bool isThinClientFeatureFlagEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); + internal bool isThinClientEnabled; // Gateway has backoff/retry logic to hide transient errors. private RetryPolicy retryPolicy; @@ -430,8 +431,8 @@ internal DocumentClient(Uri serviceEndpoint, transportClientHandlerFactory, storeClientFactory) { - } - + } + /// /// Initializes a new instance of the class using the /// specified service endpoint, an authorization key (or resource token) and a connection policy @@ -456,6 +457,7 @@ internal DocumentClient(Uri serviceEndpoint, /// This is distributed tracing flag /// This is the chaos interceptor used for fault injection /// A boolean flag indicating if stack trace optimization is enabled. + /// A boolean flag indicating if length-aware range comparators should be used for EPK range comparisons. /// /// The service endpoint can be obtained from the Azure Management Portal. /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal @@ -485,7 +487,8 @@ internal DocumentClient(Uri serviceEndpoint, RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, IChaosInterceptorFactory chaosInterceptorFactory = null, - bool enableAsyncCacheExceptionNoSharing = true) + bool enableAsyncCacheExceptionNoSharing = true, + bool useLengthAwareRangeComparer = false) { if (sendingRequestEventArgs != null) { @@ -513,6 +516,7 @@ internal DocumentClient(Uri serviceEndpoint, enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); this.chaosInterceptorFactory = chaosInterceptorFactory; this.chaosInterceptor = chaosInterceptorFactory?.CreateInterceptor(this); + this.UseLengthAwareRangeComparer = useLengthAwareRangeComparer; this.Initialize( serviceEndpoint: serviceEndpoint, @@ -956,8 +960,8 @@ internal virtual void Initialize(Uri serviceEndpoint, ServicePointAccessor servicePoint = ServicePointAccessor.FindServicePoint(this.ServiceEndpoint); servicePoint.ConnectionLimit = this.ConnectionPolicy.MaxConnectionLimit; } -#endif - this.GlobalEndpointManager = new GlobalEndpointManager(this, this.ConnectionPolicy, this.enableAsyncCacheExceptionNoSharing); +#endif + this.GlobalEndpointManager = new GlobalEndpointManager(this, this.ConnectionPolicy, this.enableAsyncCacheExceptionNoSharing); this.httpClient = CosmosHttpClientCore.CreateWithConnectionPolicy( this.ApiType, @@ -1050,47 +1054,46 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli if (this.desiredConsistencyLevel.HasValue) { this.EnsureValidOverwrite(this.desiredConsistencyLevel.Value); - } - - if (!this.ConnectionPolicy.DisablePartitionLevelFailoverClientLevelOverride - && this.accountServiceConfiguration != null && this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.HasValue) - { - this.ConnectionPolicy.EnablePartitionLevelFailover = this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.Value; - } - - this.isThinClientEnabled = (this.accountServiceConfiguration.AccountProperties?.ThinClientWritableLocationsInternal?.Count ?? 0) > 0; - - this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker |= this.ConnectionPolicy.EnablePartitionLevelFailover; - this.ConnectionPolicy.UserAgentContainer.AppendFeatures(this.GetUserAgentFeatures()); - this.InitializePartitionLevelFailoverWithDefaultHedging(); - - this.PartitionKeyRangeLocation = - this.ConnectionPolicy.EnablePartitionLevelFailover - || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker - ? new GlobalPartitionEndpointManagerCore( - this.GlobalEndpointManager, - this.ConnectionPolicy.EnablePartitionLevelFailover, - this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, - this.isThinClientEnabled) - : GlobalPartitionEndpointManagerNoOp.Instance; - + } + + if (!this.ConnectionPolicy.DisablePartitionLevelFailoverClientLevelOverride + && this.accountServiceConfiguration != null && this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.HasValue) + { + this.ConnectionPolicy.EnablePartitionLevelFailover = this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.Value; + } + + this.isThinClientEnabled = this.isThinClientFeatureFlagEnabled && (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway) && + (this.accountServiceConfiguration.AccountProperties?.ThinClientWritableLocationsInternal?.Count ?? 0) > 0; + this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker |= this.ConnectionPolicy.EnablePartitionLevelFailover; + this.ConnectionPolicy.UserAgentContainer.AppendFeatures(this.GetUserAgentFeatures()); + this.InitializePartitionLevelFailoverWithDefaultHedging(); + + this.PartitionKeyRangeLocation = + new GlobalPartitionEndpointManagerCore( + this.GlobalEndpointManager, + this.ConnectionPolicy.EnablePartitionLevelFailover, + this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, + this.isThinClientEnabled); + this.retryPolicy = new RetryPolicy( globalEndpointManager: this.GlobalEndpointManager, connectionPolicy: this.ConnectionPolicy, - partitionKeyRangeLocationCache: this.PartitionKeyRangeLocation, + partitionKeyRangeLocationCache: this.PartitionKeyRangeLocation, isThinClientEnabled: this.isThinClientEnabled); this.ResetSessionTokenRetryPolicy = this.retryPolicy; GatewayStoreModel gatewayStoreModel = new GatewayStoreModel( - this.GlobalEndpointManager, - this.sessionContainer, - (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, - this.eventSource, - this.serializerSettings, - this.httpClient, - this.PartitionKeyRangeLocation, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); + endpointManager: this.GlobalEndpointManager, + sessionContainer: this.sessionContainer, + defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, + eventSource: this.eventSource, + serializerSettings: this.serializerSettings, + httpClient: this.httpClient, + globalPartitionEndpointManager: this.PartitionKeyRangeLocation, + isThinClientEnabled: this.isThinClientEnabled, + userAgentContainer: this.ConnectionPolicy.UserAgentContainer, + this.chaosInterceptor); this.GatewayStoreModel = gatewayStoreModel; @@ -1101,34 +1104,14 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli retryPolicy: this.retryPolicy, telemetryToServiceHelper: this.telemetryToServiceHelper, enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); + this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.UseLengthAwareRangeComparer, this.enableAsyncCacheExceptionNoSharing); this.ResetSessionTokenRetryPolicy = new ResetSessionTokenRetryPolicyFactory(this.sessionContainer, this.collectionCache, this.retryPolicy); - gatewayStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); - - if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway) - { - if (this.isThinClientEnabled) - { - ThinClientStoreModel thinClientStoreModel = new ( - endpointManager: this.GlobalEndpointManager, - this.PartitionKeyRangeLocation, - this.sessionContainer, - (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, - this.eventSource, - this.serializerSettings, - this.httpClient, - this.ConnectionPolicy.UserAgentContainer, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); - - thinClientStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); - - this.StoreModel = thinClientStoreModel; - } - else - { - this.StoreModel = this.GatewayStoreModel; - } + gatewayStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); + + if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway) + { + this.StoreModel = this.GatewayStoreModel; } else { @@ -1251,6 +1234,8 @@ internal ApiType ApiType internal bool UseMultipleWriteLocations { get; private set; } + internal bool UseLengthAwareRangeComparer { get; private set; } + /// /// Gets the endpoint Uri for the service endpoint from the Azure Cosmos DB service. /// @@ -1416,6 +1401,7 @@ public void Dispose() if (this.GlobalEndpointManager != null) { + this.GlobalEndpointManager.OnEnablePartitionLevelFailoverConfigChanged -= this.UpdatePartitionLevelFailoverConfigWithAccountRefresh; this.GlobalEndpointManager.Dispose(); this.GlobalEndpointManager = null; } @@ -6558,6 +6544,14 @@ await this.cosmosAuthorization.AddAuthorizationHeaderAsync( "GET", AuthorizationTokenType.PrimaryMasterKey); + // Added the thinclient endpoint discovery header for account data refresh requests. + // This header signals to the service that the client supports thin client mode + // and needs thinclient-specific endpoint information in the response. + if (this.isThinClientFeatureFlagEnabled) + { + headersCollection[ThinClientConstants.EnableThinClientEndpointDiscoveryHeaderName] = true.ToString(); + } + foreach (string key in headersCollection.AllKeys()) { request.Headers.Add(key, headersCollection[key]); @@ -6798,6 +6792,8 @@ private void InitializeDirectConnectivity(IStoreClientFactory storeClientFactory private void CreateStoreModel(bool subscribeRntbdStatus) { + AccountConfigurationProperties accountConfigurationProperties = new (EnableNRegionSynchronousCommit: this.accountServiceConfiguration.AccountProperties.EnableNRegionSynchronousCommit); + //EnableReadRequestsFallback, if not explicity set on the connection policy, //is false if the account's consistency is bounded staleness, //and true otherwise. @@ -6811,7 +6807,7 @@ private void CreateStoreModel(bool subscribeRntbdStatus) !this.enableRntbdChannel, this.UseMultipleWriteLocations && (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.Strong), true, - enableReplicaValidation: this.isReplicaAddressValidationEnabled, + enableReplicaValidation: this.isReplicaAddressValidationEnabled, sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions); if (subscribeRntbdStatus) @@ -6839,58 +6835,112 @@ private async Task InitializeGatewayConfigurationReaderAsync() connectionPolicy: this.ConnectionPolicy, httpClient: this.httpClient, cancellationToken: this.cancellationTokenSource.Token, - isThinClientEnabled: this.isThinClientEnabled); + isThinClientEnabled: this.isThinClientFeatureFlagEnabled); this.accountServiceConfiguration = new CosmosAccountServiceConfiguration(accountReader.InitializeReaderAsync); - await this.accountServiceConfiguration.InitializeAsync(); - AccountProperties accountProperties = this.accountServiceConfiguration.AccountProperties; + await this.accountServiceConfiguration.InitializeAsync(); + AccountProperties accountProperties = this.accountServiceConfiguration.AccountProperties; this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && accountProperties.EnableMultipleWriteLocations; this.GlobalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(accountProperties); + this.GlobalEndpointManager.OnEnablePartitionLevelFailoverConfigChanged += this.UpdatePartitionLevelFailoverConfigWithAccountRefresh; + } + + internal string GetUserAgentFeatures() + { + int featureFlag = 0; + if (this.ConnectionPolicy.EnablePartitionLevelFailover) + { + featureFlag += (int)UserAgentFeatureFlags.PerPartitionAutomaticFailover; + } + + if (this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker) + { + featureFlag += (int)UserAgentFeatureFlags.PerPartitionCircuitBreaker; + } + + if (this.isThinClientEnabled) + { + featureFlag += (int)UserAgentFeatureFlags.ThinClient; + } + + if (ConfigurationManager.IsBinaryEncodingEnabled()) + { + featureFlag += (int)UserAgentFeatureFlags.BinaryEncoding; + } + + return featureFlag == 0 ? string.Empty : $"F{featureFlag:X}"; + } + + internal void InitializePartitionLevelFailoverWithDefaultHedging() + { + if (this.ConnectionPolicy.EnablePartitionLevelFailover + && this.ConnectionPolicy.AvailabilityStrategy == null) + { + // The default threshold is the minimum value of 1 second and a fraction (currently it's half) of + // the request timeout value provided by the end customer. + double defaultThresholdInMillis; + + if (this.ConnectionPolicy.RequestTimeout.TotalMilliseconds == 0) + { + // If the request timeout is 0, we will use the default hedging theshold value + defaultThresholdInMillis = DocumentClient.DefaultHedgingThresholdInMilliseconds; + DefaultTrace.TraceWarning("DocumentClient: Request timeout is set to 0, which is not a valid value. Falling back to default hedging threshold of {0} ms", defaultThresholdInMillis); + } + else + { + defaultThresholdInMillis = Math.Min( + DocumentClient.DefaultHedgingThresholdInMilliseconds, + this.ConnectionPolicy.RequestTimeout.TotalMilliseconds / 2); + } + + this.ConnectionPolicy.AvailabilityStrategy = AvailabilityStrategy.SDKDefaultCrossRegionHedgingStrategyForPPAF( + threshold: TimeSpan.FromMilliseconds(defaultThresholdInMillis), + thresholdStep: TimeSpan.FromMilliseconds(DocumentClient.DefaultHedgingThresholdStepInMilliseconds)); + } + } + + private void UpdatePartitionLevelFailoverConfigWithAccountRefresh( + bool isEnabled) + { + // Only update if client-level override is not disabled + if (this.ConnectionPolicy.DisablePartitionLevelFailoverClientLevelOverride) + { + DefaultTrace.TraceInformation("DocumentClient: PPAF change ignored due to client-level override disabled"); + return; + } + + DefaultTrace.TraceInformation( + "DocumentClient: PPAF Account Level Config Updated. Updating EnablePartitionLevelFailover to {0}", + isEnabled); + + // Step 1: Enable partition level failover. + this.PartitionKeyRangeLocation.SetIsPPAFEnabled(isEnabled); + this.ConnectionPolicy.EnablePartitionLevelFailover = isEnabled; + + // Step 2: Enable partition level circuit breaker. + this.PartitionKeyRangeLocation.SetIsPPCBEnabled(isEnabled); + this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker = isEnabled; + + // Step 3: Enable default hedging strategy if partition level failover is enabled. + if (isEnabled && this.ConnectionPolicy.AvailabilityStrategy == null) + { + this.InitializePartitionLevelFailoverWithDefaultHedging(); + } + else + { + if (this.ConnectionPolicy.AvailabilityStrategy is CrossRegionHedgingAvailabilityStrategy strategy && strategy.IsSDKDefaultStrategyForPPAF) + { + // If the user has not set a custom availability strategy, then we will reset it to null. + this.ConnectionPolicy.AvailabilityStrategy = null; + } + } + + // Step 4: Update the user agent features. + this.ConnectionPolicy.UserAgentContainer.AppendFeatures(this.GetUserAgentFeatures()); + + DefaultTrace.TraceInformation("DocumentClient: Successfully updated PPAF configuration dynamically"); } - - internal string GetUserAgentFeatures() - { - int featureFlag = 0; - if (this.ConnectionPolicy.EnablePartitionLevelFailover) - { - featureFlag += (int)UserAgentFeatureFlags.PerPartitionAutomaticFailover; - } - - if (this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker) - { - featureFlag += (int)UserAgentFeatureFlags.PerPartitionCircuitBreaker; - } - - if (this.isThinClientEnabled) - { - featureFlag += (int)UserAgentFeatureFlags.ThinClient; - } - - if (ConfigurationManager.IsBinaryEncodingEnabled()) - { - featureFlag += (int)UserAgentFeatureFlags.BinaryEncoding; - } - - return featureFlag == 0 ? string.Empty : $"F{featureFlag:X}"; - } - - internal void InitializePartitionLevelFailoverWithDefaultHedging() - { - if (this.ConnectionPolicy.EnablePartitionLevelFailover - && this.ConnectionPolicy.AvailabilityStrategy == null) - { - // The default threshold is the minimum value of 1 second and a fraction (currently it's half) of - // the request timeout value provided by the end customer. - double defaultThresholdInMillis = Math.Min( - DocumentClient.DefaultHedgingThresholdInMilliseconds, - this.ConnectionPolicy.RequestTimeout.TotalMilliseconds / 2); - - this.ConnectionPolicy.AvailabilityStrategy = AvailabilityStrategy.CrossRegionHedgingStrategy( - threshold: TimeSpan.FromMilliseconds(defaultThresholdInMillis), - thresholdStep: TimeSpan.FromMilliseconds(DocumentClient.DefaultHedgingThresholdStepInMilliseconds)); - } - } internal void CaptureSessionToken(DocumentServiceRequest request, DocumentServiceResponse response) { diff --git a/Microsoft.Azure.Cosmos/src/Fluent/Settings/VectorIndexDefinition.cs b/Microsoft.Azure.Cosmos/src/Fluent/Settings/VectorIndexDefinition.cs index 8bf1310187..d12f3e8f44 100644 --- a/Microsoft.Azure.Cosmos/src/Fluent/Settings/VectorIndexDefinition.cs +++ b/Microsoft.Azure.Cosmos/src/Fluent/Settings/VectorIndexDefinition.cs @@ -50,6 +50,26 @@ public VectorIndexDefinition Path( return this; } + /// + /// Configures the quantizer type for the current definition. + /// + /// + /// The quantizer type to be used for vector quantization. This is an optional parameter and applies to index + /// types DiskANN and quantizedFlat. Allowed values are Product and Spherical. + /// + /// An instance of the current . +#if PREVIEW + public +#else + internal +#endif + VectorIndexDefinition WithQuantizerType( + QuantizerType quantizerType) + { + this.vectorIndexPath.QuantizerType = quantizerType; + return this; + } + /// /// Configures the quantization byte size for the current definition. /// diff --git a/Microsoft.Azure.Cosmos/src/GatewayStoreClient.cs b/Microsoft.Azure.Cosmos/src/GatewayStoreClient.cs index 5701316ac6..2d333b3964 100644 --- a/Microsoft.Azure.Cosmos/src/GatewayStoreClient.cs +++ b/Microsoft.Azure.Cosmos/src/GatewayStoreClient.cs @@ -22,8 +22,8 @@ namespace Microsoft.Azure.Cosmos internal class GatewayStoreClient : TransportClient { - private readonly bool isPartitionLevelFailoverEnabled; private readonly ICommunicationEventSource eventSource; + private readonly GlobalPartitionEndpointManager globalPartitionEndpointManager; protected readonly CosmosHttpClient httpClient; protected readonly JsonSerializerSettings SerializerSettings; @@ -32,13 +32,13 @@ internal class GatewayStoreClient : TransportClient public GatewayStoreClient( CosmosHttpClient httpClient, ICommunicationEventSource eventSource, - JsonSerializerSettings serializerSettings = null, - bool isPartitionLevelFailoverEnabled = false) + GlobalPartitionEndpointManager globalPartitionEndpointManager, + JsonSerializerSettings serializerSettings = null) { this.httpClient = httpClient; this.SerializerSettings = serializerSettings; this.eventSource = eventSource; - this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; + this.globalPartitionEndpointManager = globalPartitionEndpointManager; } public async Task InvokeAsync( @@ -386,10 +386,16 @@ private Task InvokeClientAsync( resourceType, HttpTimeoutPolicy.GetTimeoutPolicy( request, - this.isPartitionLevelFailoverEnabled), + this.IsPartitionLevelFailoverEnabled()), request.RequestContext.ClientRequestStatistics, cancellationToken, request); } + + private bool IsPartitionLevelFailoverEnabled() + { + return this.globalPartitionEndpointManager.IsPartitionLevelCircuitBreakerEnabled() + || this.globalPartitionEndpointManager.IsPartitionLevelAutomaticFailoverEnabled(); + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs index 22a79eefc6..c4c8b434a4 100644 --- a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs @@ -10,7 +10,6 @@ namespace Microsoft.Azure.Cosmos using System.Linq; using System.Net; using System.Net.Http; - using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Common; @@ -19,47 +18,65 @@ namespace Microsoft.Azure.Cosmos using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; + using Microsoft.Azure.Documents.FaultInjection; using Newtonsoft.Json; // Marking it as non-sealed in order to unit test it using Moq framework internal class GatewayStoreModel : IStoreModelExtension, IDisposable { - private readonly bool isPartitionLevelFailoverEnabled; + private readonly bool isThinClientEnabled; private static readonly string sessionConsistencyAsString = ConsistencyLevel.Session.ToString(); private readonly GlobalPartitionEndpointManager globalPartitionEndpointManager; + protected internal readonly ISessionContainer sessionContainer; + private readonly DocumentClientEventSource eventSource; + private readonly IChaosInterceptor chaosInterceptor; internal readonly GlobalEndpointManager endpointManager; - private readonly DocumentClientEventSource eventSource; internal readonly ConsistencyLevel defaultConsistencyLevel; + // Store Clients to send requests to the gateway and/ or thin client endpoints. + private ThinClientStoreClient thinClientStoreClient; private GatewayStoreClient gatewayStoreClient; // Caches to resolve the PartitionKeyRange from request. For Session Token Optimization. - protected PartitionKeyRangeCache partitionKeyRangeCache; - protected ClientCollectionCache clientCollectionCache; - protected ISessionContainer sessionContainer; + protected internal PartitionKeyRangeCache partitionKeyRangeCache; + protected internal ClientCollectionCache clientCollectionCache; public GatewayStoreModel( - GlobalEndpointManager endpointManager, - ISessionContainer sessionContainer, - ConsistencyLevel defaultConsistencyLevel, - DocumentClientEventSource eventSource, - JsonSerializerSettings serializerSettings, - CosmosHttpClient httpClient, - GlobalPartitionEndpointManager globalPartitionEndpointManager, - bool isPartitionLevelFailoverEnabled = false) + GlobalEndpointManager endpointManager, + ISessionContainer sessionContainer, + ConsistencyLevel defaultConsistencyLevel, + DocumentClientEventSource eventSource, + JsonSerializerSettings serializerSettings, + CosmosHttpClient httpClient, + GlobalPartitionEndpointManager globalPartitionEndpointManager, + bool isThinClientEnabled, + UserAgentContainer userAgentContainer = null, + IChaosInterceptor chaosInterceptor = null) { - this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; this.endpointManager = endpointManager; this.sessionContainer = sessionContainer; this.defaultConsistencyLevel = defaultConsistencyLevel; this.eventSource = eventSource; this.globalPartitionEndpointManager = globalPartitionEndpointManager; + this.chaosInterceptor = chaosInterceptor; this.gatewayStoreClient = new GatewayStoreClient( httpClient, this.eventSource, - serializerSettings, - isPartitionLevelFailoverEnabled); + globalPartitionEndpointManager, + serializerSettings); + this.isThinClientEnabled = isThinClientEnabled; + + if (isThinClientEnabled) + { + this.thinClientStoreClient = new ThinClientStoreClient( + httpClient, + userAgentContainer, + this.eventSource, + globalPartitionEndpointManager, + serializerSettings, + this.chaosInterceptor); + } this.globalPartitionEndpointManager.SetBackgroundConnectionPeriodicRefreshTask( this.MarkEndpointsToHealthyAsync); @@ -67,6 +84,8 @@ public GatewayStoreModel( public virtual async Task ProcessMessageAsync(DocumentServiceRequest request, CancellationToken cancellationToken = default) { + DocumentServiceResponse response; + await GatewayStoreModel.ApplySessionTokenAsync( request, this.defaultConsistencyLevel, @@ -74,20 +93,19 @@ await GatewayStoreModel.ApplySessionTokenAsync( this.partitionKeyRangeCache, this.clientCollectionCache, this.endpointManager); - - DocumentServiceResponse response; try { - // Collect region name only for document resources - if (request.ResourceType.Equals(ResourceType.Document) && this.endpointManager.TryGetLocationForGatewayDiagnostics(request.RequestContext.LocationEndpointToRoute, out string regionName)) + if (request.ResourceType.Equals(ResourceType.Document) && + this.endpointManager.TryGetLocationForGatewayDiagnostics(request.RequestContext.LocationEndpointToRoute, out string regionName)) { request.RequestContext.RegionName = regionName; } + bool isPPAFEnabled = this.IsPartitionLevelFailoverEnabled(); // This is applicable for both per partition automatic failover and per partition circuit breaker. - if (this.isPartitionLevelFailoverEnabled + if ((isPPAFEnabled || this.isThinClientEnabled) && !ReplicatedResourceClient.IsMasterResource(request.ResourceType) - && request.ResourceType.IsPartitioned()) + && (request.ResourceType.IsPartitioned() || request.ResourceType == ResourceType.StoredProcedure)) { (bool isSuccess, PartitionKeyRange partitionKeyRange) = await TryResolvePartitionKeyRangeAsync( request: request, @@ -97,17 +115,50 @@ await GatewayStoreModel.ApplySessionTokenAsync( refreshCache: false); request.RequestContext.ResolvedPartitionKeyRange = partitionKeyRange; - this.globalPartitionEndpointManager.TryAddPartitionLevelLocationOverride(request); + + if (isPPAFEnabled) + { + this.globalPartitionEndpointManager.TryAddPartitionLevelLocationOverride(request); + } } - Uri physicalAddress = GatewayStoreClient.IsFeedRequest(request.OperationType) ? this.GetFeedUri(request) : this.GetEntityUri(request); - response = await this.gatewayStoreClient.InvokeAsync(request, request.ResourceType, physicalAddress, cancellationToken); + bool canUseThinClient = + this.thinClientStoreClient != null && + GatewayStoreModel.IsOperationSupportedByThinClient(request); + + Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) + ? this.GetFeedUri(request) + : this.GetEntityUri(request); + + if (canUseThinClient) + { + Uri thinClientEndpoint = this.endpointManager.ResolveThinClientEndpoint(request); + + AccountProperties account = await this.GetDatabaseAccountPropertiesAsync(); + + response = await this.thinClientStoreClient.InvokeAsync( + request, + request.ResourceType, + physicalAddress, + thinClientEndpoint, + account.Id, + this.clientCollectionCache, + cancellationToken); + } + else + { + response = await this.gatewayStoreClient.InvokeAsync( + request, + request.ResourceType, + physicalAddress, + cancellationToken); + } } catch (DocumentClientException exception) { if ((!ReplicatedResourceClient.IsMasterResource(request.ResourceType)) && (exception.StatusCode == HttpStatusCode.PreconditionFailed || exception.StatusCode == HttpStatusCode.Conflict - || (exception.StatusCode == HttpStatusCode.NotFound && exception.GetSubStatus() != SubStatusCodes.ReadSessionNotAvailable))) + || (exception.StatusCode == HttpStatusCode.NotFound && exception.GetSubStatus() != SubStatusCodes.ReadSessionNotAvailable))) { await this.CaptureSessionTokenAndHandleSplitAsync(exception.StatusCode, exception.GetSubStatus(), request, exception.Headers); } @@ -368,7 +419,13 @@ internal static async Task> TryResolveSessionTokenAsync( return new Tuple(false, null); } - protected static async Task> TryResolvePartitionKeyRangeAsync( + private bool IsPartitionLevelFailoverEnabled() + { + return this.globalPartitionEndpointManager.IsPartitionLevelCircuitBreakerEnabled() + || this.globalPartitionEndpointManager.IsPartitionLevelAutomaticFailoverEnabled(); + } + + internal static async Task> TryResolvePartitionKeyRangeAsync( DocumentServiceRequest request, ISessionContainer sessionContainer, PartitionKeyRangeCache partitionKeyRangeCache, @@ -494,10 +551,60 @@ internal static bool IsStoredProcedureCrudOperation( operationType != Documents.OperationType.ExecuteJavaScript; } + internal static bool IsOperationSupportedByThinClient(DocumentServiceRequest request) + { + // Document operations + if (request.ResourceType == ResourceType.Document + && (request.OperationType == OperationType.Batch + || request.OperationType == OperationType.Patch + || request.OperationType == OperationType.Create + || request.OperationType == OperationType.Read + || request.OperationType == OperationType.Upsert + || request.OperationType == OperationType.Replace + || request.OperationType == OperationType.Delete + || request.OperationType == OperationType.Query)) + { + return true; + } + + // Stored Procedure execution + if (request.ResourceType == ResourceType.StoredProcedure + && request.OperationType == OperationType.ExecuteJavaScript) + { + return true; + } + + return false; + } + private async Task GetDatabaseAccountPropertiesAsync() + { + AccountProperties accountProperties = await this.endpointManager.GetDatabaseAccountAsync(); + if (accountProperties != null) + { + return accountProperties; + } + + throw new InvalidOperationException("Failed to retrieve AccountProperties. The response was null."); + } + protected virtual void Dispose(bool disposing) { if (disposing) { + if (this.thinClientStoreClient != null) + { + try + { + this.thinClientStoreClient.Dispose(); + } + catch (Exception exception) + { + DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", + exception.Message); + } + + this.thinClientStoreClient = null; + } if (this.gatewayStoreClient != null) { try diff --git a/Microsoft.Azure.Cosmos/src/Handler/RequestMessage.cs b/Microsoft.Azure.Cosmos/src/Handler/RequestMessage.cs index aa6d796df2..f9f5d4b640 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/RequestMessage.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/RequestMessage.cs @@ -385,9 +385,13 @@ private bool AssertPartitioningPropertiesAndHeaders() bool partitionKeyRangeIdExists = !string.IsNullOrEmpty(this.Headers.PartitionKeyRangeId); if (partitionKeyRangeIdExists) - { + { + OperationType operationType = this.OperationType; // Assert operation type is not write - if (this.OperationType != OperationType.Query && this.OperationType != OperationType.ReadFeed && this.OperationType != OperationType.Batch) + if (operationType != OperationType.Query + && operationType != OperationType.QueryPlan + && operationType != OperationType.ReadFeed + && operationType != OperationType.Batch) { throw new ArgumentOutOfRangeException(RMResources.UnexpectedPartitionKeyRangeId); } diff --git a/Microsoft.Azure.Cosmos/src/Headers/CosmosMessageHeadersInternal.cs b/Microsoft.Azure.Cosmos/src/Headers/CosmosMessageHeadersInternal.cs index 1956f201e3..ea459a6e01 100644 --- a/Microsoft.Azure.Cosmos/src/Headers/CosmosMessageHeadersInternal.cs +++ b/Microsoft.Azure.Cosmos/src/Headers/CosmosMessageHeadersInternal.cs @@ -5,11 +5,8 @@ namespace Microsoft.Azure.Cosmos { using System; - using System.Collections; using System.Collections.Generic; - using System.Collections.Specialized; using System.Globalization; - using System.Text; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; diff --git a/Microsoft.Azure.Cosmos/src/Headers/NameValueResponseHeaders.cs b/Microsoft.Azure.Cosmos/src/Headers/NameValueResponseHeaders.cs index 2edf5d484a..0ffece2d04 100644 --- a/Microsoft.Azure.Cosmos/src/Headers/NameValueResponseHeaders.cs +++ b/Microsoft.Azure.Cosmos/src/Headers/NameValueResponseHeaders.cs @@ -5,11 +5,7 @@ namespace Microsoft.Azure.Cosmos { using System; - using System.Collections; using System.Collections.Generic; - using System.Collections.Specialized; - using System.Globalization; - using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; /// diff --git a/Microsoft.Azure.Cosmos/src/Headers/StoreRequestHeaders.cs b/Microsoft.Azure.Cosmos/src/Headers/StoreRequestHeaders.cs index b907363685..81e883214c 100644 --- a/Microsoft.Azure.Cosmos/src/Headers/StoreRequestHeaders.cs +++ b/Microsoft.Azure.Cosmos/src/Headers/StoreRequestHeaders.cs @@ -4,9 +4,7 @@ namespace Microsoft.Azure.Cosmos { - using System; using System.Collections.Generic; - using System.Collections.Specialized; using Microsoft.Azure.Documents.Collections; /// diff --git a/Microsoft.Azure.Cosmos/src/Headers/StoreResponseHeaders.cs b/Microsoft.Azure.Cosmos/src/Headers/StoreResponseHeaders.cs index 42b96fc847..0f6b19e414 100644 --- a/Microsoft.Azure.Cosmos/src/Headers/StoreResponseHeaders.cs +++ b/Microsoft.Azure.Cosmos/src/Headers/StoreResponseHeaders.cs @@ -6,7 +6,6 @@ namespace Microsoft.Azure.Cosmos { using System; using System.Collections.Generic; - using System.Collections.Specialized; using Microsoft.Azure.Documents.Collections; /// diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs b/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs index 75b42b2bf9..26c1c92665 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs @@ -359,7 +359,6 @@ private async Task SendHttpHelperAsync( { if (this.chaosInterceptor != null && documentServiceRequest != null) { - this.SetFaultInjectionHeader(documentServiceRequest, requestMessage); (bool hasFault, HttpResponseMessage fiResponseMessage) = await this.InjectFaultsAsync(cancellationTokenSource, documentServiceRequest, requestMessage); if (hasFault) { @@ -374,7 +373,6 @@ private async Task SendHttpHelperAsync( if (this.chaosInterceptor != null && documentServiceRequest != null) { - this.SetFaultInjectionHeader(documentServiceRequest, requestMessage); CancellationToken fiToken = cancellationTokenSource.Token; fiToken.ThrowIfCancellationRequested(); await this.chaosInterceptor.OnAfterHttpSendAsync(documentServiceRequest, fiToken); @@ -390,7 +388,7 @@ private async Task SendHttpHelperAsync( return responseMessage; } - bool isOutOfRetries = CosmosHttpClientCore.IsOutOfRetries(timeoutPolicy, startDateTimeUtc, timeoutEnumerator); + bool isOutOfRetries = CosmosHttpClientCore.IsOutOfRetries(timeoutEnumerator); if (isOutOfRetries) { return responseMessage; @@ -404,7 +402,7 @@ private async Task SendHttpHelperAsync( datum.RecordHttpException(requestMessage, e, resourceType, requestStartTime); trace = datum.Trace; } - bool isOutOfRetries = CosmosHttpClientCore.IsOutOfRetries(timeoutPolicy, startDateTimeUtc, timeoutEnumerator); + bool isOutOfRetries = CosmosHttpClientCore.IsOutOfRetries(timeoutEnumerator); switch (e) { @@ -417,7 +415,7 @@ private async Task SendHttpHelperAsync( // Convert OperationCanceledException to 408 when the HTTP client throws it. This makes it clear that the // the request timed out and was not user canceled operation. - if (isOutOfRetries || !timeoutPolicy.IsSafeToRetry(requestMessage.Method)) + if (isOutOfRetries || !CosmosHttpClientCore.IsSafeToRetry(documentServiceRequest)) { // throw current exception (caught in transport handler) string message = @@ -442,14 +440,14 @@ private async Task SendHttpHelperAsync( break; case WebException webException: - if (isOutOfRetries || (!timeoutPolicy.IsSafeToRetry(requestMessage.Method) && !WebExceptionUtility.IsWebExceptionRetriable(webException))) + if (isOutOfRetries || (!CosmosHttpClientCore.IsSafeToRetry(documentServiceRequest) && !WebExceptionUtility.IsWebExceptionRetriable(webException))) { throw; } break; case HttpRequestException httpRequestException: - if (isOutOfRetries || !timeoutPolicy.IsSafeToRetry(requestMessage.Method)) + if (isOutOfRetries || !CosmosHttpClientCore.IsSafeToRetry(documentServiceRequest)) { throw; } @@ -469,11 +467,6 @@ private async Task SendHttpHelperAsync( } } - private void SetFaultInjectionHeader(DocumentServiceRequest documentServiceRequest, HttpRequestMessage requestMessage) - { - documentServiceRequest.Headers.Set("FAULTINJECTION_GW_URI", requestMessage.RequestUri.ToString()); - } - private async Task<(bool, HttpResponseMessage)> InjectFaultsAsync( CancellationTokenSource cancellationTokenSource, DocumentServiceRequest documentServiceRequest, @@ -500,13 +493,24 @@ private void SetFaultInjectionHeader(DocumentServiceRequest documentServiceReque } private static bool IsOutOfRetries( - HttpTimeoutPolicy timeoutPolicy, - DateTime startDateTimeUtc, IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> timeoutEnumerator) { return !timeoutEnumerator.MoveNext(); // No more retries are configured } + private static bool IsSafeToRetry(DocumentServiceRequest documentServiceRequest) + { + // Three scenarios are safely retriable: + // 1) If request is null since they are originated from GetAsync calls + // 2) If request is read-only + // 3) If request is an address request. + if (documentServiceRequest == null) + { + return true; + } + return documentServiceRequest.IsReadOnlyRequest || documentServiceRequest.ResourceType == ResourceType.Address; + } + private async Task ExecuteHttpHelperAsync( HttpRequestMessage requestMessage, ResourceType resourceType, diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicy.cs b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicy.cs index 1620e1b36c..f5eb179c66 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicy.cs @@ -13,7 +13,6 @@ internal abstract class HttpTimeoutPolicy public abstract string TimeoutPolicyName { get; } public abstract int TotalRetryCount { get; } public abstract IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> GetTimeoutEnumerator(); - public abstract bool IsSafeToRetry(HttpMethod httpMethod); public abstract bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, HttpResponseMessage responseMessage); @@ -49,16 +48,30 @@ public static HttpTimeoutPolicy GetTimeoutPolicy( { if (isThinClientEnabled) { - return documentServiceRequest.IsReadOnlyRequest - ? HttpTimeoutPolicyForThinClient.InstanceShouldRetryAndThrow503OnTimeout - : HttpTimeoutPolicyForThinClient.InstanceShouldNotRetryAndThrow503OnTimeout; + if (documentServiceRequest.IsReadOnlyRequest) + { + return documentServiceRequest.OperationType == OperationType.Read + ? HttpTimeoutPolicyForThinClient.InstanceShouldRetryAndThrow503OnTimeoutForPointReads + : HttpTimeoutPolicyForThinClient.InstanceShouldRetryAndThrow503OnTimeoutForNonPointReads; + } + else + { + return HttpTimeoutPolicyForThinClient.InstanceShouldNotRetryAndThrow503OnTimeoutForWrites; + } } // Data Plane Reads. else if (documentServiceRequest.IsReadOnlyRequest) { - return isPartitionLevelFailoverEnabled - ? HttpTimeoutPolicyForPartitionFailover.InstanceShouldThrow503OnTimeout - : HttpTimeoutPolicyDefault.InstanceShouldThrow503OnTimeout; + if (isPartitionLevelFailoverEnabled) + { + return documentServiceRequest.OperationType == OperationType.Read + ? HttpTimeoutPolicyForPartitionFailover.InstanceShouldThrow503OnTimeoutForPointReads + : HttpTimeoutPolicyForPartitionFailover.InstanceShouldThrow503OnTimeoutForNonPointReads; + } + else + { + return HttpTimeoutPolicyDefault.InstanceShouldThrow503OnTimeout; + } } } diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyControlPlaneRead.cs b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyControlPlaneRead.cs index 9f85ba33ab..02a29b99ad 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyControlPlaneRead.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyControlPlaneRead.cs @@ -32,12 +32,6 @@ private HttpTimeoutPolicyControlPlaneRead() return this.TimeoutsAndDelays.GetEnumerator(); } - // This is for control plane reads which should always be safe to retry on. - public override bool IsSafeToRetry(HttpMethod httpMethod) - { - return true; - } - public override bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, HttpResponseMessage responseMessage) { return false; diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyControlPlaneRetriableHotPath.cs b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyControlPlaneRetriableHotPath.cs index 53a7c78e9e..8143b49015 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyControlPlaneRetriableHotPath.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyControlPlaneRetriableHotPath.cs @@ -36,13 +36,6 @@ private HttpTimeoutPolicyControlPlaneRetriableHotPath(bool shouldThrow503OnTimeo return this.TimeoutsAndDelays.GetEnumerator(); } - // The hot path should always be safe to retires since it should be retrieving meta data - // information that is not idempotent. - public override bool IsSafeToRetry(HttpMethod httpMethod) - { - return true; - } - public override bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, HttpResponseMessage responseMessage) { if (responseMessage == null) @@ -55,11 +48,6 @@ public override bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, Ht return false; } - if (!this.IsSafeToRetry(requestHttpMethod)) - { - return false; - } - return true; } diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyDefault.cs b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyDefault.cs index 69a83707a1..15d4aaa6c3 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyDefault.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyDefault.cs @@ -36,13 +36,6 @@ private HttpTimeoutPolicyDefault(bool shouldThrow503OnTimeout) return this.TimeoutsAndDelays.GetEnumerator(); } - // Assume that it is not safe to retry unless it is a get method. - // Create and other operations could have succeeded even though a timeout occurred. - public override bool IsSafeToRetry(HttpMethod httpMethod) - { - return httpMethod == HttpMethod.Get; - } - public override bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, HttpResponseMessage responseMessage) { return false; diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyForPartitionFailover.cs b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyForPartitionFailover.cs index 3e623a5aed..c93bde7cd4 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyForPartitionFailover.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyForPartitionFailover.cs @@ -9,37 +9,40 @@ namespace Microsoft.Azure.Cosmos internal sealed class HttpTimeoutPolicyForPartitionFailover : HttpTimeoutPolicy { - public static readonly HttpTimeoutPolicy Instance = new HttpTimeoutPolicyForPartitionFailover(false); - public static readonly HttpTimeoutPolicy InstanceShouldThrow503OnTimeout = new HttpTimeoutPolicyForPartitionFailover(true); - public bool shouldThrow503OnTimeout; + public static readonly HttpTimeoutPolicy InstanceShouldThrow503OnTimeoutForNonPointReads = new HttpTimeoutPolicyForPartitionFailover(isPointRead: false); + public static readonly HttpTimeoutPolicy InstanceShouldThrow503OnTimeoutForPointReads = new HttpTimeoutPolicyForPartitionFailover(isPointRead: true); + private readonly bool isPointRead; private static readonly string Name = nameof(HttpTimeoutPolicyDefault); - private HttpTimeoutPolicyForPartitionFailover(bool shouldThrow503OnTimeout) + private HttpTimeoutPolicyForPartitionFailover(bool isPointRead) { - this.shouldThrow503OnTimeout = shouldThrow503OnTimeout; + this.isPointRead = isPointRead; } - private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelays = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>() + // Timeouts and delays are based on the following rationale: + // For point reads: 3 attempts with timeouts of 6s, 6s, and 10s respectively. + // For non-point reads: 3 attempts with timeouts of 6s, 6s, and 10s respectively. + private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelaysForPointReads = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>() { - (TimeSpan.FromSeconds(.5), TimeSpan.Zero), - (TimeSpan.FromSeconds(.5), TimeSpan.Zero), - (TimeSpan.FromSeconds(1), TimeSpan.Zero), + (TimeSpan.FromSeconds(6), TimeSpan.Zero), + (TimeSpan.FromSeconds(6), TimeSpan.Zero), + (TimeSpan.FromSeconds(10), TimeSpan.Zero), + }; + + private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelaysForNonPointReads = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>() + { + (TimeSpan.FromSeconds(6), TimeSpan.Zero), + (TimeSpan.FromSeconds(6), TimeSpan.Zero), + (TimeSpan.FromSeconds(10), TimeSpan.Zero), }; public override string TimeoutPolicyName => HttpTimeoutPolicyForPartitionFailover.Name; - public override int TotalRetryCount => this.TimeoutsAndDelays.Count; + public override int TotalRetryCount => this.isPointRead ? this.TimeoutsAndDelaysForPointReads.Count : this.TimeoutsAndDelaysForNonPointReads.Count; public override IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> GetTimeoutEnumerator() { - return this.TimeoutsAndDelays.GetEnumerator(); - } - - // Assume that it is not safe to retry unless it is a get method. - // Create and other operations could have succeeded even though a timeout occurred. - public override bool IsSafeToRetry(HttpMethod httpMethod) - { - return httpMethod == HttpMethod.Get; + return this.isPointRead ? this.TimeoutsAndDelaysForPointReads.GetEnumerator() : this.TimeoutsAndDelaysForNonPointReads.GetEnumerator(); } public override bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, HttpResponseMessage responseMessage) @@ -47,6 +50,6 @@ public override bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, Ht return false; } - public override bool ShouldThrow503OnTimeout => this.shouldThrow503OnTimeout; + public override bool ShouldThrow503OnTimeout => true; } } diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyForThinClient.cs b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyForThinClient.cs index 91201a8538..7693c5940f 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyForThinClient.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyForThinClient.cs @@ -11,37 +11,43 @@ internal sealed class HttpTimeoutPolicyForThinClient : HttpTimeoutPolicy { public bool shouldRetry; public bool shouldThrow503OnTimeout; + public bool isPointRead; private static readonly string Name = nameof(HttpTimeoutPolicyForThinClient); - public static readonly HttpTimeoutPolicy InstanceShouldRetryAndThrow503OnTimeout = new HttpTimeoutPolicyForThinClient(true, true); - public static readonly HttpTimeoutPolicy InstanceShouldNotRetryAndThrow503OnTimeout = new HttpTimeoutPolicyForThinClient(true, false); + public static readonly HttpTimeoutPolicy InstanceShouldRetryAndThrow503OnTimeoutForPointReads = new HttpTimeoutPolicyForThinClient(shouldThrow503OnTimeout: true, shouldRetry: true, isPointRead: true); + public static readonly HttpTimeoutPolicy InstanceShouldRetryAndThrow503OnTimeoutForNonPointReads = new HttpTimeoutPolicyForThinClient(shouldThrow503OnTimeout: true, shouldRetry: true, isPointRead: false); + public static readonly HttpTimeoutPolicy InstanceShouldNotRetryAndThrow503OnTimeoutForWrites = new HttpTimeoutPolicyForThinClient(shouldThrow503OnTimeout: true, shouldRetry: false, isPointRead: false); private HttpTimeoutPolicyForThinClient( bool shouldThrow503OnTimeout, - bool shouldRetry) + bool shouldRetry, + bool isPointRead) { this.shouldThrow503OnTimeout = shouldThrow503OnTimeout; this.shouldRetry = shouldRetry; + this.isPointRead = isPointRead; } - private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelays = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>() + private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelaysForPointReads = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>() { - (TimeSpan.FromSeconds(.5), TimeSpan.Zero), - (TimeSpan.FromSeconds(1), TimeSpan.Zero), - (TimeSpan.FromSeconds(5), TimeSpan.Zero), + (TimeSpan.FromSeconds(6), TimeSpan.Zero), + (TimeSpan.FromSeconds(6), TimeSpan.Zero), + (TimeSpan.FromSeconds(10), TimeSpan.Zero), + }; + + private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelaysForNonPointReads = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>() + { + (TimeSpan.FromSeconds(6), TimeSpan.Zero), + (TimeSpan.FromSeconds(6), TimeSpan.Zero), + (TimeSpan.FromSeconds(10), TimeSpan.Zero), }; public override string TimeoutPolicyName => HttpTimeoutPolicyForThinClient.Name; - public override int TotalRetryCount => this.TimeoutsAndDelays.Count; + public override int TotalRetryCount => this.isPointRead ? this.TimeoutsAndDelaysForPointReads.Count : this.TimeoutsAndDelaysForNonPointReads.Count; public override IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> GetTimeoutEnumerator() { - return this.TimeoutsAndDelays.GetEnumerator(); - } - - public override bool IsSafeToRetry(HttpMethod httpMethod) - { - return this.shouldRetry; + return this.isPointRead ? this.TimeoutsAndDelaysForPointReads.GetEnumerator() : this.TimeoutsAndDelaysForNonPointReads.GetEnumerator(); } public override bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, HttpResponseMessage responseMessage) @@ -56,7 +62,7 @@ public override bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, Ht return false; } - if (!this.IsSafeToRetry(requestHttpMethod)) + if (!this.shouldRetry) { return false; } diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyNoRetry.cs b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyNoRetry.cs index 65ea12144b..cb4c48011b 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyNoRetry.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyNoRetry.cs @@ -31,12 +31,6 @@ private HttpTimeoutPolicyNoRetry() return this.TimeoutsAndDelays.GetEnumerator(); } - // Always Unsafe to retry - public override bool IsSafeToRetry(HttpMethod httpMethod) - { - return false; - } - public override bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, HttpResponseMessage responseMessage) { return false; diff --git a/Microsoft.Azure.Cosmos/src/Inference/InferenceService.cs b/Microsoft.Azure.Cosmos/src/Inference/InferenceService.cs new file mode 100644 index 0000000000..6f808fa691 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Inference/InferenceService.cs @@ -0,0 +1,209 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + using System.Net.Http; + using System.Net.Http.Headers; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using global::Azure.Core; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Collections; + + /// + /// Provides functionality to interact with the Cosmos DB Inference Service for semantic reranking. + /// + internal class InferenceService : IDisposable + { + // Base path for the inference service endpoint. + private const string basePath = "/inference/semanticReranking"; + // User agent string for inference requests. + private const string inferenceUserAgent = "cosmos-inference-dotnet"; + // Default scope for AAD authentication. + private const string inferenceServiceDefaultScope = "https://dbinference.azure.com/.default"; + private const int inferenceServiceDefaultMaxConnectionLimit = 50; + + private readonly int inferenceServiceMaxConnectionLimit; + private readonly string inferenceServiceBaseUrl; + private readonly Uri inferenceEndpoint; + + private HttpClient httpClient; + private AuthorizationTokenProvider cosmosAuthorization; + + private bool disposedValue; + + /// + /// Initializes a new instance of the class. + /// + /// The CosmosClient instance. + /// Thrown if AAD authentication is not used. + public InferenceService(CosmosClient client) + { + this.inferenceServiceBaseUrl = ConfigurationManager.GetEnvironmentVariable("AZURE_COSMOS_SEMANTIC_RERANKER_INFERENCE_ENDPOINT", null); + + if (string.IsNullOrEmpty(this.inferenceServiceBaseUrl)) + { + throw new ArgumentNullException("Set environment variable AZURE_COSMOS_SEMANTIC_RERANKER_INFERENCE_ENDPOINT to use inference service"); + } + + this.inferenceServiceMaxConnectionLimit = ConfigurationManager.GetEnvironmentVariable( + "AZURE_COSMOS_SEMANTIC_RERANKER_INFERENCE_SERVICE_MAX_CONNECTION_LIMIT", + inferenceServiceDefaultMaxConnectionLimit) ?? inferenceServiceDefaultMaxConnectionLimit; + + // Create and configure HttpClient for inference requests. + HttpMessageHandler httpMessageHandler = CosmosHttpClientCore.CreateHttpClientHandler( + gatewayModeMaxConnectionLimit: this.inferenceServiceMaxConnectionLimit, + webProxy: null, + serverCertificateCustomValidationCallback: client.DocumentClient.ConnectionPolicy.ServerCertificateCustomValidationCallback); + + this.httpClient = new HttpClient(httpMessageHandler); + + this.CreateClientHelper(this.httpClient); + + // Construct the inference service endpoint URI. + this.inferenceEndpoint = new Uri($"{this.inferenceServiceBaseUrl}/{basePath}"); + + // Ensure AAD authentication is used. + if (client.DocumentClient.cosmosAuthorization.GetType() != typeof(AuthorizationTokenProviderTokenCredential)) + { + throw new InvalidOperationException("InferenceService only supports AAD authentication."); + } + + // Set up token credential for authorization. + // This is done to ensure the correct scope, which is different than the scope of the client, is used for the inference service. + AuthorizationTokenProviderTokenCredential defaultOperationTokenProvider = client.DocumentClient.cosmosAuthorization as AuthorizationTokenProviderTokenCredential; + TokenCredential tokenCredential = defaultOperationTokenProvider.tokenCredential; + + this.cosmosAuthorization = new AuthorizationTokenProviderTokenCredential( + tokenCredential: tokenCredential, + accountEndpoint: new Uri(inferenceServiceDefaultScope), + backgroundTokenCredentialRefreshInterval: client.ClientOptions?.TokenCredentialBackgroundRefreshInterval); + } + + /// + /// Sends a semantic rerank request to the inference service. + /// + /// The context/query for reranking. + /// The documents to be reranked. + /// Optional additional options for the request. + /// Cancellation token. + /// A dictionary containing the reranked results. + public async Task SemanticRerankAsync( + string rerankContext, + IEnumerable documents, + IDictionary options = null, + CancellationToken cancellationToken = default) + { + // Prepare HTTP request for semantic reranking. + HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, this.inferenceEndpoint); + INameValueCollection additionalHeaders = new RequestNameValueCollection(); + await this.cosmosAuthorization.AddInferenceAuthorizationHeaderAsync( + headersCollection: additionalHeaders, + this.inferenceEndpoint, + HttpConstants.HttpMethods.Post, + AuthorizationTokenType.AadToken); + additionalHeaders.Add(HttpConstants.HttpHeaders.UserAgent, inferenceUserAgent); + + // Add all headers to the HTTP request. + foreach (string key in additionalHeaders.AllKeys()) + { + message.Headers.Add(key, additionalHeaders[key]); + } + + // Build the request payload. + Dictionary body = this.AddSemanticRerankPayload(rerankContext, documents, options); + + message.Content = new StringContent( + Newtonsoft.Json.JsonConvert.SerializeObject(body), + Encoding.UTF8, + RuntimeConstants.MediaTypes.Json); + + // Send the request and ensure success. + HttpResponseMessage responseMessage = await this.httpClient.SendAsync(message, cancellationToken); + responseMessage.EnsureSuccessStatusCode(); + + // Deserialize and return the response content as a dictionary. + return await SemanticRerankResult.DeserializeSemanticRerankResultAsync(responseMessage); + } + + /// + /// Configures the provided HttpClient with default headers and settings for inference requests. + /// + /// The HttpClient to configure. + private void CreateClientHelper(HttpClient httpClient) + { + httpClient.Timeout = TimeSpan.FromSeconds(120); + httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true }; + + // Set requested API version header for version enforcement. + httpClient.DefaultRequestHeaders.Add(HttpConstants.HttpHeaders.Version, + HttpConstants.Versions.CurrentVersion); + + httpClient.DefaultRequestHeaders.Add(HttpConstants.HttpHeaders.Accept, RuntimeConstants.MediaTypes.Json); + } + + /// + /// Constructs the payload for the semantic rerank request. + /// + /// The context/query for reranking. + /// The documents to be reranked. + /// Optional additional options. + /// A dictionary representing the request payload. + private Dictionary AddSemanticRerankPayload(string rerankContext, IEnumerable documents, IDictionary options) + { + Dictionary payload = new Dictionary + { + { "query", rerankContext }, + { "documents", documents.ToArray() } + }; + + if (options == null) + { + return payload; + } + + // Add any additional options to the payload. + foreach (string option in options.Keys) + { + payload.Add(option, options[option]); + } + + return payload; + } + + /// + /// Disposes managed resources used by the service. + /// + /// Indicates if called from Dispose. + protected void Dispose(bool disposing) + { + if (!this.disposedValue) + { + if (disposing) + { + this.httpClient.Dispose(); + this.cosmosAuthorization.Dispose(); + this.httpClient = null; + this.cosmosAuthorization = null; + } + + this.disposedValue = true; + } + } + + /// + /// Disposes the service and its resources. + /// + public void Dispose() + { + this.Dispose(true); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Inference/RerankScore.cs b/Microsoft.Azure.Cosmos/src/Inference/RerankScore.cs new file mode 100644 index 0000000000..15c7200b7d --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Inference/RerankScore.cs @@ -0,0 +1,46 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + /// + /// Represents the score assigned to a document after a reranking operation. + /// +#if PREVIEW + public +#else + internal +#endif + + class RerankScore + { + /// + /// Gets the document content or identifier that was reranked. + /// + public string Document { get; } + + /// + /// Gets the score assigned to the document after reranking. + /// + public double Score { get; } + + /// + /// Gets the original index or position of the document before reranking. + /// + public int Index { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The document content or identifier. + /// The reranked score for the document. + /// The original index of the document. + public RerankScore(string document, double score, int index) + { + this.Document = document; + this.Score = score; + this.Index = index; + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Inference/SemanticRerankResult.cs b/Microsoft.Azure.Cosmos/src/Inference/SemanticRerankResult.cs new file mode 100644 index 0000000000..0f1208d7e8 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Inference/SemanticRerankResult.cs @@ -0,0 +1,133 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System.Collections.Generic; + using System.IO; + using System.Net.Http; + using System.Net.Http.Headers; + using System.Text.Json; + using System.Threading.Tasks; + + /// + /// Represents the result of a semantic reranking operation, including rerank scores, + /// latency, token usage, and HTTP response headers. + /// +#if PREVIEW + public +#else + internal +#endif + + class SemanticRerankResult + { + /// + /// Gets the HTTP response headers associated with the rerank operation. + /// + public HttpResponseHeaders Headers { get; } + + /// + /// Gets the list of rerank scores for the documents. + /// + public IReadOnlyList RerankScores { get; } + + /// + /// Gets the latency information for the rerank operation. + /// + public Dictionary Latency { get; } + + /// + /// Gets the token usage information for the rerank operation. + /// + public Dictionary TokenUseage { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The list of rerank scores. + /// The latency information. + /// The token usage information. + /// The HTTP response headers. + private SemanticRerankResult( + IReadOnlyList rerankScores, + Dictionary latency, + Dictionary tokenUseage, + HttpResponseHeaders headers) + { + this.RerankScores = rerankScores; + this.Latency = latency; + this.TokenUseage = tokenUseage; + this.Headers = headers; + } + + /// + /// Deserializes a from an HTTP response message asynchronously. + /// + /// The HTTP response message containing the rerank result. + /// A task that represents the asynchronous operation. The task result contains the deserialized . + internal static async Task DeserializeSemanticRerankResultAsync(HttpResponseMessage responseMessage) + { + Stream content = await responseMessage.Content.ReadAsStreamAsync(); + + using (content) + { + using (JsonDocument doc = await JsonDocument.ParseAsync(content)) + { + JsonElement root = doc.RootElement; + + // Parse Scores + List rerankScores = new List(); + if (root.TryGetProperty("Scores", out JsonElement scoresElement) && scoresElement.ValueKind == JsonValueKind.Array) + { + foreach (JsonElement item in scoresElement.EnumerateArray()) + { + string document = string.Empty; + if (item.TryGetProperty("document", out JsonElement docElement)) + { + // Try to deserialize as an object + switch (docElement.ValueKind) + { + case JsonValueKind.Object: + document = docElement.GetRawText(); + break; + case JsonValueKind.String: + document = docElement.GetString(); + break; + default: + break; + } + } + + double score = item.TryGetProperty("score", out JsonElement scoreElement) && scoreElement.TryGetDouble(out double s) ? s : 0.0; + int index = item.TryGetProperty("index", out JsonElement indexElement) && indexElement.TryGetInt32(out int i) ? i : -1; + + rerankScores.Add(new RerankScore(document, score, index)); + } + } + + // Parse latency + Dictionary latency = null; + if (root.TryGetProperty("latency", out JsonElement latencyElement) && latencyElement.ValueKind == JsonValueKind.Object) + { + latency = JsonSerializer.Deserialize>(latencyElement.GetRawText()); + } + + // Parse token_usage + Dictionary tokenUsage = null; + if (root.TryGetProperty("token_usage", out JsonElement tokenUsageElement) && tokenUsageElement.ValueKind == JsonValueKind.Object) + { + tokenUsage = JsonSerializer.Deserialize>(tokenUsageElement.GetRawText()); + } + + return new SemanticRerankResult( + rerankScores, + latency, + tokenUsage, + responseMessage.Headers); + } + } + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Json/Interop/CosmosDBToNewtonsoftWriter.cs b/Microsoft.Azure.Cosmos/src/Json/Interop/CosmosDBToNewtonsoftWriter.cs index f8e4e289c1..c527d3d721 100644 --- a/Microsoft.Azure.Cosmos/src/Json/Interop/CosmosDBToNewtonsoftWriter.cs +++ b/Microsoft.Azure.Cosmos/src/Json/Interop/CosmosDBToNewtonsoftWriter.cs @@ -328,7 +328,20 @@ public override void WriteValue(decimal value) /// The value to write. public override void WriteValue(DateTime value) { - // We use rount trip format for datetime parsing and trim the additional trailing zeros using a custom "O" format + // We use round trip format for datetime parsing and trim the additional trailing zeros using a custom "O" format + // to maintain milliseconds precision. + this.WriteValue( + value.ToString( + format: CosmosDBToNewtonsoftWriter.RoundTripFormatWithoutTrailingZeros)); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTimeOffset value) + { + // We use round trip format for datetime parsing and trim the additional trailing zeros using a custom "O" format // to maintain milliseconds precision. this.WriteValue( value.ToString( diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Chars.cs b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Chars.cs index 4ac5756c85..42e45734e9 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Chars.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Chars.cs @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.Json using System; using System.Collections; using System.Collections.Immutable; - using Microsoft.Azure.Documents; internal static partial class JsonBinaryEncoding { @@ -305,5 +304,138 @@ private static StringCompressionLookupTables Create(char[] list, byte[] charSet, return new StringCompressionLookupTables(list.ToImmutableArray(), new BitArray(charSet), charToByte.ToImmutableArray(), byteToTwoChars.ToImmutableArray()); } } + + public readonly struct Base64StringEncodingLookupTables + { + public static readonly Base64StringEncodingLookupTables StdBase64 = Base64StringEncodingLookupTables.Create( + list: new char[] + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', + }, + charSet: new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xFF, 0x03, + 0xFE, 0xFF, 0xFF, 0x07, 0xFE, 0xFF, 0xFF, 0x07 + }, + charToByte: new byte[] + { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 0, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }); + + public static readonly Base64StringEncodingLookupTables UrlBase64 = Base64StringEncodingLookupTables.Create( + list: new char[] + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '_', + }, + charSet: new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFF, 0x03, + 0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07 + }, + charToByte: new byte[] + { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 0, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }); + + private Base64StringEncodingLookupTables( + ImmutableArray list, + BitArray bitmap, + ImmutableArray charToByte) + { + if (list.Length != 64) + { + throw new ArgumentException($"{nameof(list)} must be length 64."); + } + + if (bitmap == null) + { + throw new ArgumentNullException(nameof(bitmap)); + } + + if (bitmap.Length != 128) + { + throw new ArgumentException($"{nameof(bitmap)} must be length 128."); + } + + if (charToByte.Length != 256) + { + throw new ArgumentException($"{nameof(charToByte)} must be length 256."); + } + + this.List = list; + this.Bitmap = bitmap; + this.CharToByte = charToByte; + } + + public ImmutableArray List { get; } + + public BitArray Bitmap { get; } + + public ImmutableArray CharToByte { get; } + + private static Base64StringEncodingLookupTables Create(char[] list, byte[] charSet, byte[] charToByte) + { + if (list == null) + { + throw new ArgumentNullException(nameof(list)); + } + + if (charSet == null) + { + throw new ArgumentNullException(nameof(charSet)); + } + + if (charToByte == null) + { + throw new ArgumentNullException(nameof(charToByte)); + } + + return new Base64StringEncodingLookupTables(list.ToImmutableArray(), new BitArray(charSet), charToByte.ToImmutableArray()); + } + } } } diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Enumerator.cs b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Enumerator.cs index ef7798656e..8ee03fae3f 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Enumerator.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Enumerator.cs @@ -6,8 +6,6 @@ namespace Microsoft.Azure.Cosmos.Json { using System; using System.Collections.Generic; - using System.Linq; - using System.Runtime.InteropServices; internal static partial class JsonBinaryEncoding { diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.FirstValueOffsets.cs b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.FirstValueOffsets.cs index 36070e533c..4844b992e5 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.FirstValueOffsets.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.FirstValueOffsets.cs @@ -48,13 +48,13 @@ private static class FirstValueOffsets // String Values [0x70, 0x78) 0, // 0x70 - 0, // 0x71 - 0, // 0x72 - 0, // 0x73 - 0, // 0x74 - 0, // StrGL (Lowercase GUID string) - 0, // StrGU (Uppercase GUID string) - 0, // StrGQ (Double-quoted lowercase GUID string) + 0, // Standard Base64-encoded string with 1-byte length and 1-byte padding length + 0, // Standard Base64-encoded string with 2-byte length and 1-byte padding length + 0, // URL-safe Base64-encoded string with 1-byte length and 1-byte padding length + 0, // URL-safe Base64-encoded string with 2-byte length and 1-byte padding length + 0, // Lowercase GUID string + 0, // Uppercase GUID string + 0, // Double-quoted lowercase GUID string // Compressed strings [0x78, 0x80) 0, // String 1-byte length - Lowercase hexadecimal digits encoded as 4-bit characters diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.NodeTypes.cs b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.NodeTypes.cs index 613d793baf..79302d23ca 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.NodeTypes.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.NodeTypes.cs @@ -63,13 +63,13 @@ public static class NodeTypes // String Values [0x70, 0x78) Unknown, // 0x70 - Unknown, // 0x71 - Unknown, // 0x72 - Unknown, // 0x73 - Unknown, // 0x74 - String, // StrGL (Lowercase GUID string) - String, // StrGU (Uppercase GUID string) - String, // StrGQ (Double-quoted lowercase GUID string) + String, // Standard Base64-encoded string with 1-byte length and 1-byte padding length + String, // Standard Base64-encoded string with 2-byte length and 1-byte padding length + String, // URL-safe Base64-encoded string with 1-byte length and 1-byte padding length + String, // URL-safe Base64-encoded string with 2-byte length and 1-byte padding length + String, // Lowercase GUID string + String, // Uppercase GUID string + String, // Double-quoted lowercase GUID string // Compressed strings [0x78, 0x80) String, // String 1-byte length - Lowercase hexadecimal digits encoded as 4-bit characters diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Strings.cs b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Strings.cs index 9488f12500..d4793d3147 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Strings.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Strings.cs @@ -64,13 +64,13 @@ internal static partial class JsonBinaryEncoding // String Values [0x70, 0x78) false, // 0x70 - false, // 0x71 - false, // 0x72 - false, // 0x73 - false, // 0x74 - false, // StrGL (Lowercase GUID string) - false, // StrGU (Uppercase GUID string) - false, // StrGQ (Double-quoted lowercase GUID string) + false, // Standard Base64-encoded string with 1-byte length and 1-byte padding length + false, // Standard Base64-encoded string with 2-byte length and 1-byte padding length + false, // URL-safe Base64-encoded string with 1-byte length and 1-byte padding length + false, // URL-safe Base64-encoded string with 2-byte length and 1-byte padding length + false, // Lowercase GUID string + false, // Uppercase GUID string + false, // Double-quoted lowercase GUID string // Compressed strings [falsex78, falsex8false) false, // String 1-byte length - Lowercase hexadecimal digits encoded as 4-bit characters @@ -270,11 +270,13 @@ private static ReadOnlyMemory GetUtf8MemoryValue( return bufferedStringValue.Memory; } - if (JsonBinaryEncoding.TypeMarker.IsCompressedString(typeMarker) || JsonBinaryEncoding.TypeMarker.IsGuidString(typeMarker)) + if (JsonBinaryEncoding.TypeMarker.IsCompressedString(typeMarker) || + JsonBinaryEncoding.TypeMarker.IsGuidString(typeMarker) || + JsonBinaryEncoding.TypeMarker.IsBase64String(typeMarker)) { DecodeString(stringToken.Span, Span.Empty, out int valueLength); Memory bytes = new byte[valueLength]; - DecodeString(stringToken.Span, bytes.Span, out valueLength); + DecodeString(stringToken.Span, bytes.Span, out _); return bytes; } @@ -317,7 +319,9 @@ private static void GetStringValue( valueLength = bufferedStringValue.Length; } - else if (JsonBinaryEncoding.TypeMarker.IsCompressedString(typeMarker) || JsonBinaryEncoding.TypeMarker.IsGuidString(typeMarker)) + else if (JsonBinaryEncoding.TypeMarker.IsCompressedString(typeMarker) || + JsonBinaryEncoding.TypeMarker.IsGuidString(typeMarker) || + JsonBinaryEncoding.TypeMarker.IsBase64String(typeMarker)) { DecodeString(stringToken.Span, destinationBuffer, out valueLength); } @@ -452,7 +456,7 @@ private static bool TryGetBufferedLengthPrefixedString( if (JsonBinaryEncoding.TypeMarker.IsEncodedLengthString(typeMarker)) { start = JsonBinaryEncoding.TypeMarkerLength; - length = JsonBinaryEncoding.GetStringLengths(typeMarker); + length = typeMarker - JsonBinaryEncoding.TypeMarker.EncodedStringLengthMin; } else { @@ -612,7 +616,7 @@ private static bool TryGetEncodedSystemStringTypeMarker( /// Try Get Encoded User String Type Marker /// /// The value. - /// The optional json string dictionary. + /// The optional JSON string dictionary. /// The multi byte type marker if found. /// Whether or not the Encoded User String Type Marker was found. private static bool TryGetEncodedUserStringTypeMarker( @@ -632,7 +636,7 @@ private static bool TryGetEncodedUserStringTypeMarker( return false; } - // Convert the stringId to a multibyte type marker + // Convert the stringId to a multi-byte type marker const byte OneByteCount = TypeMarker.UserString1ByteLengthMax - TypeMarker.UserString1ByteLengthMin; const int TwoByteCount = (TypeMarker.UserString2ByteLengthMax - TypeMarker.UserString2ByteLengthMin) * 256; const int MaxStringId = OneByteCount + TwoByteCount; @@ -754,23 +758,52 @@ public static bool TryEncodeGuidString(ReadOnlySpan guidString, Span return true; } + private enum Base64Encoding + { + None, + StdBase64, + UrlBase64 + } + public static bool TryEncodeCompressedString( ReadOnlySpan stringValue, Span destinationBuffer, + bool enableBase64Strings, out int bytesWritten) { - if (destinationBuffer.Length < MinCompressedStringLength) + if (stringValue.Length < MinCompressedStringLength) { bytesWritten = default; return false; } + // Determine if the string is a base64 candidate + enableBase64Strings &= IsBase64StringCandidate(stringValue); + + // If the string length is a multiple of 4, we need to check whether it's padded with '=' + int padding = 0; + if (enableBase64Strings && ((stringValue.Length % 4) == 0)) + { + // We either have one or two padding characters at the end of the string + if (stringValue[stringValue.Length - 1] == '=') + { + padding++; + if (stringValue[stringValue.Length - 2] == '=') + { + padding++; + } + } + } + + int valueLength = stringValue.Length - padding; + int firstSetBit = 128; int lastSetBit = 0; int charCount = 0; - BitArray valueCharSet = new BitArray(length: 128); + // Create a bit-set with all the ASCII character of the string value - for (int index = 0; index < stringValue.Length; index++) + BitArray valueCharSet = new BitArray(length: 128); + for (int index = 0; index < valueLength; index++) { byte charValue = stringValue[index]; @@ -792,7 +825,36 @@ public static bool TryEncodeCompressedString( valueCharSet.Set(charValue, true); } - int charRange = (lastSetBit - firstSetBit) + 1; + // First, check if the string is a valid base64 string + Base64Encoding base64Encoding; + if (enableBase64Strings) + { + // Check if the string is a valid Base64 string + base64Encoding = valueCharSet.IsSubset(Base64StringEncodingLookupTables.StdBase64.Bitmap) + ? Base64Encoding.StdBase64 + : valueCharSet.IsSubset(Base64StringEncodingLookupTables.UrlBase64.Bitmap) ? Base64Encoding.UrlBase64 : Base64Encoding.None; + } + else + { + base64Encoding = Base64Encoding.None; + } + + // Undo base64 padding if present + if (padding > 0) + { + // Register the padding characters + const char charValue = '='; + if (!valueCharSet[charValue]) + { + charCount++; + firstSetBit = Math.Min(charValue, firstSetBit); + lastSetBit = Math.Max(charValue, lastSetBit); + } + + valueCharSet.Set(charValue, true); + } + + int charRange = lastSetBit - firstSetBit + 1; // Attempt to encode the string as 4-bit packed values over a defined character set if ((stringValue.Length <= 0xFF) && (charCount <= 16) && (stringValue.Length >= Min4BitCharSetStringLength)) @@ -838,6 +900,16 @@ public static bool TryEncodeCompressedString( } } + // Attempt to encode the string as a Base64 string + if (base64Encoding != Base64Encoding.None) + { + bool isUrlBase64 = base64Encoding == Base64Encoding.UrlBase64; + if (TryConvertBase64StringToBytes(stringValue, isUrlBase64, destinationBuffer, out bytesWritten)) + { + return true; + } + } + // Try encode the string as 7 bit packed characters with no base value if (stringValue.Length >= MinCompressedStringLength7) { @@ -1143,30 +1215,70 @@ private static void DecodeString(ReadOnlySpan stringToken, Span dest bool isDateTimeString = JsonBinaryEncoding.TypeMarker.IsDateTimeString(typeMarker); bool isCompressedString = JsonBinaryEncoding.TypeMarker.IsCompressedString(typeMarker); bool isGuidString = JsonBinaryEncoding.TypeMarker.IsGuidString(typeMarker); + bool isBase64String = JsonBinaryEncoding.TypeMarker.IsBase64String(typeMarker); - if (!(isHexadecimalString || isDateTimeString || isCompressedString || isGuidString)) + if (!(isHexadecimalString || isDateTimeString || isCompressedString || isGuidString || isBase64String)) { - throw new ArgumentException("token must be a hex, datetime, compressed, or guid string."); + throw new ArgumentException("Token must be a Hexadecimal, DateTime, Compressed, Guid, or Base64 string."); } - int lengthByteCount = (isHexadecimalString || isDateTimeString) ? 1 : (isCompressedString ? ((typeMarker == JsonBinaryEncoding.TypeMarker.Packed7BitStringLength2) ? 2 : 1) : 0); - int baseCharByteCount = JsonBinaryEncoding.TypeMarker.InRange(typeMarker, JsonBinaryEncoding.TypeMarker.Packed4BitString, TypeMarker.Packed6BitString + 1) ? 1 : 0; - int prefixByteCount = TypeMarkerLength + lengthByteCount + baseCharByteCount; + int lengthByteCount; + switch (typeMarker) + { + case JsonBinaryEncoding.TypeMarker.LowercaseGuidString: + case JsonBinaryEncoding.TypeMarker.UppercaseGuidString: + case JsonBinaryEncoding.TypeMarker.DoubleQuotedLowercaseGuidString: + lengthByteCount = 0; + break; + case JsonBinaryEncoding.TypeMarker.Base64StringLength1: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength1: + case JsonBinaryEncoding.TypeMarker.CompressedLowercaseHexString: + case JsonBinaryEncoding.TypeMarker.CompressedUppercaseHexString: + case JsonBinaryEncoding.TypeMarker.CompressedDateTimeString: + case JsonBinaryEncoding.TypeMarker.Packed4BitString: + case JsonBinaryEncoding.TypeMarker.Packed5BitString: + case JsonBinaryEncoding.TypeMarker.Packed6BitString: + case JsonBinaryEncoding.TypeMarker.Packed7BitStringLength1: + lengthByteCount = 1; + break; + case JsonBinaryEncoding.TypeMarker.Base64StringLength2: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength2: + case JsonBinaryEncoding.TypeMarker.Packed7BitStringLength2: + lengthByteCount = 2; + break; + default: + throw new ArgumentOutOfRangeException($"Unexpected type marker: {typeMarker}."); + } - if (stringToken.Length < prefixByteCount) + int extraByteCount; + switch (typeMarker) { - throw new JsonInvalidTokenException(); + case JsonBinaryEncoding.TypeMarker.Packed4BitString: + case JsonBinaryEncoding.TypeMarker.Packed5BitString: + case JsonBinaryEncoding.TypeMarker.Packed6BitString: + extraByteCount = 1; // base char + break; + case JsonBinaryEncoding.TypeMarker.Base64StringLength1: + case JsonBinaryEncoding.TypeMarker.Base64StringLength2: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength1: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength2: + extraByteCount = 1; // padding byte + break; + default: + extraByteCount = 0; + break; } - bytesWritten = GetEncodedStringValueLength(stringToken); - int encodedLength = GetEncodedStringBufferLength(stringToken); - byte baseChar = GetEncodedStringBaseChar(stringToken); + int prefixByteCount = TypeMarkerLength + lengthByteCount + extraByteCount; - if (stringToken.Length < (prefixByteCount + encodedLength)) + if (stringToken.Length < prefixByteCount) { throw new JsonInvalidTokenException(); } + bytesWritten = GetEncodedStringValueLength(stringToken); + int encodedLength = GetEncodedStringBufferLength(stringToken); + if (!destinationBuffer.IsEmpty) { if (bytesWritten > destinationBuffer.Length) @@ -1174,9 +1286,7 @@ private static void DecodeString(ReadOnlySpan stringToken, Span dest throw new InvalidOperationException("buffer is too small"); } - ReadOnlySpan encodedString = stringToken.Slice(start: prefixByteCount, length: encodedLength); - - DecodeStringValue(typeMarker, encodedString, baseChar, destinationBuffer.Slice(start: 0, length: bytesWritten)); + DecodeStringValue(stringToken, prefixByteCount, encodedLength, destinationBuffer.Slice(start: 0, length: bytesWritten)); } } @@ -1186,6 +1296,21 @@ private static int GetEncodedStringValueLength(ReadOnlySpan stringToken) switch (typeMarker) { + case JsonBinaryEncoding.TypeMarker.Base64StringLength1: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength1: + return JsonBinaryEncoding.ValueLengths.ComputeBase64StringLength(stringToken[1], stringToken[2]); + + case JsonBinaryEncoding.TypeMarker.Base64StringLength2: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength2: + return JsonBinaryEncoding.ValueLengths.ComputeBase64StringLength(GetFixedSizedValue(stringToken.Slice(start: 1)), stringToken[3]); + + case JsonBinaryEncoding.TypeMarker.LowercaseGuidString: + case JsonBinaryEncoding.TypeMarker.UppercaseGuidString: + return GuidLength; + + case JsonBinaryEncoding.TypeMarker.DoubleQuotedLowercaseGuidString: + return GuidWithQuotesLength; + case JsonBinaryEncoding.TypeMarker.CompressedLowercaseHexString: case JsonBinaryEncoding.TypeMarker.CompressedUppercaseHexString: case JsonBinaryEncoding.TypeMarker.CompressedDateTimeString: @@ -1198,13 +1323,6 @@ private static int GetEncodedStringValueLength(ReadOnlySpan stringToken) case JsonBinaryEncoding.TypeMarker.Packed7BitStringLength2: return GetFixedSizedValue(stringToken.Slice(start: 1)); - case JsonBinaryEncoding.TypeMarker.LowercaseGuidString: - case JsonBinaryEncoding.TypeMarker.UppercaseGuidString: - return GuidLength; - - case JsonBinaryEncoding.TypeMarker.DoubleQuotedLowercaseGuidString: - return GuidWithQuotesLength; - default: throw new ArgumentOutOfRangeException($"Unexpected type marker: {typeMarker}."); } @@ -1216,6 +1334,19 @@ private static int GetEncodedStringBufferLength(ReadOnlySpan stringToken) switch (typeMarker) { + case JsonBinaryEncoding.TypeMarker.Base64StringLength1: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength1: + return JsonBinaryEncoding.ValueLengths.GetBase64ByteCount(stringToken[1], stringToken[2]); + + case JsonBinaryEncoding.TypeMarker.Base64StringLength2: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength2: + return JsonBinaryEncoding.ValueLengths.GetBase64ByteCount(GetFixedSizedValue(stringToken.Slice(start: 1)), stringToken[3]); + + case JsonBinaryEncoding.TypeMarker.LowercaseGuidString: + case JsonBinaryEncoding.TypeMarker.UppercaseGuidString: + case JsonBinaryEncoding.TypeMarker.DoubleQuotedLowercaseGuidString: + return 16; + case JsonBinaryEncoding.TypeMarker.CompressedLowercaseHexString: case JsonBinaryEncoding.TypeMarker.CompressedUppercaseHexString: case JsonBinaryEncoding.TypeMarker.CompressedDateTimeString: @@ -1236,11 +1367,6 @@ private static int GetEncodedStringBufferLength(ReadOnlySpan stringToken) case JsonBinaryEncoding.TypeMarker.Packed7BitStringLength2: return JsonBinaryEncoding.ValueLengths.GetCompressedStringLength(GetFixedSizedValue(stringToken.Slice(1)), numberOfBits: 7); - case JsonBinaryEncoding.TypeMarker.LowercaseGuidString: - case JsonBinaryEncoding.TypeMarker.UppercaseGuidString: - case JsonBinaryEncoding.TypeMarker.DoubleQuotedLowercaseGuidString: - return 16; - default: throw new ArgumentException($"Invalid type marker: {typeMarker}"); } @@ -1252,6 +1378,17 @@ private static byte GetEncodedStringBaseChar(ReadOnlySpan stringToken) switch (typeMarker) { + case JsonBinaryEncoding.TypeMarker.Base64StringLength1: + case JsonBinaryEncoding.TypeMarker.Base64StringLength2: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength1: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength2: + return 0; + + case JsonBinaryEncoding.TypeMarker.LowercaseGuidString: + case JsonBinaryEncoding.TypeMarker.UppercaseGuidString: + case JsonBinaryEncoding.TypeMarker.DoubleQuotedLowercaseGuidString: + return 0; + case JsonBinaryEncoding.TypeMarker.CompressedLowercaseHexString: case JsonBinaryEncoding.TypeMarker.CompressedUppercaseHexString: case JsonBinaryEncoding.TypeMarker.CompressedDateTimeString: @@ -1266,78 +1403,71 @@ private static byte GetEncodedStringBaseChar(ReadOnlySpan stringToken) case JsonBinaryEncoding.TypeMarker.Packed7BitStringLength2: return 0; - case JsonBinaryEncoding.TypeMarker.LowercaseGuidString: - case JsonBinaryEncoding.TypeMarker.UppercaseGuidString: - case JsonBinaryEncoding.TypeMarker.DoubleQuotedLowercaseGuidString: - return 0; - default: throw new ArgumentException($"Invalid type marker: {typeMarker}"); } } - private static void DecodeStringValue(byte typeMarker, ReadOnlySpan encodedString, byte baseChar, Span destinationBuffer) + private static void DecodeStringValue(ReadOnlySpan encodedValue, int prefixLength, int encodedLength, Span destinationBuffer) { + if (encodedValue.Length < (prefixLength + encodedLength)) + { + throw new JsonInvalidTokenException(); + } + + byte typeMarker = encodedValue[0]; + ReadOnlySpan encodedString = encodedValue.Slice(start: prefixLength, length: encodedLength); + switch (typeMarker) { - case JsonBinaryEncoding.TypeMarker.CompressedLowercaseHexString: - if (baseChar != 0) - { - throw new InvalidOperationException("base char needs to be 0."); - } + case JsonBinaryEncoding.TypeMarker.Base64StringLength1: + case JsonBinaryEncoding.TypeMarker.Base64StringLength2: + ConvertBytesToBase64String(encodedString, padding: encodedValue[prefixLength - 1], isBase64Url: false, destinationBuffer); + break; + + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength1: + case JsonBinaryEncoding.TypeMarker.Base64UrlStringLength2: + ConvertBytesToBase64String(encodedString, padding: encodedValue[prefixLength - 1], isBase64Url: true, destinationBuffer); + break; + + case JsonBinaryEncoding.TypeMarker.LowercaseGuidString: + case JsonBinaryEncoding.TypeMarker.UppercaseGuidString: + DecodeGuidStringValue(encodedString, isUpperCaseGuid: typeMarker == JsonBinaryEncoding.TypeMarker.UppercaseGuidString, destinationBuffer); + break; + + case JsonBinaryEncoding.TypeMarker.DoubleQuotedLowercaseGuidString: + destinationBuffer[0] = (byte)'"'; + DecodeGuidStringValue(encodedString, isUpperCaseGuid: false, destinationBuffer.Slice(start: 1)); + destinationBuffer[GuidWithQuotesLength - 1] = (byte)'"'; + break; + case JsonBinaryEncoding.TypeMarker.CompressedLowercaseHexString: Decode4BitCharacterStringValue(StringCompressionLookupTables.LowercaseHex, encodedString, destinationBuffer); break; case JsonBinaryEncoding.TypeMarker.CompressedUppercaseHexString: - if (baseChar != 0) - { - throw new InvalidOperationException("base char needs to be 0."); - } - Decode4BitCharacterStringValue(StringCompressionLookupTables.UppercaseHex, encodedString, destinationBuffer); break; case JsonBinaryEncoding.TypeMarker.CompressedDateTimeString: - if (baseChar != 0) - { - throw new InvalidOperationException("base char needs to be 0."); - } - Decode4BitCharacterStringValue(StringCompressionLookupTables.DateTime, encodedString, destinationBuffer); break; case JsonBinaryEncoding.TypeMarker.Packed4BitString: - DecodeCompressedStringValue(numberOfBits: 4, encodedString, baseChar, destinationBuffer); + DecodeCompressedStringValue(numberOfBits: 4, encodedString, baseChar: encodedValue[2], destinationBuffer); break; case JsonBinaryEncoding.TypeMarker.Packed5BitString: - DecodeCompressedStringValue(numberOfBits: 5, encodedString, baseChar, destinationBuffer); + DecodeCompressedStringValue(numberOfBits: 5, encodedString, baseChar: encodedValue[2], destinationBuffer); break; case JsonBinaryEncoding.TypeMarker.Packed6BitString: - DecodeCompressedStringValue(numberOfBits: 6, encodedString, baseChar, destinationBuffer); + DecodeCompressedStringValue(numberOfBits: 6, encodedString, baseChar: encodedValue[2], destinationBuffer); break; case JsonBinaryEncoding.TypeMarker.Packed7BitStringLength1: case JsonBinaryEncoding.TypeMarker.Packed7BitStringLength2: - if (baseChar != 0) - { - throw new InvalidOperationException("base char needs to be 0."); - } - - DecodeCompressedStringValue(numberOfBits: 7, encodedString, baseChar, destinationBuffer); - break; - - case JsonBinaryEncoding.TypeMarker.LowercaseGuidString: - case JsonBinaryEncoding.TypeMarker.UppercaseGuidString: - DecodeGuidStringValue(encodedString, isUpperCaseGuid: typeMarker == JsonBinaryEncoding.TypeMarker.UppercaseGuidString, destinationBuffer); - break; - - case JsonBinaryEncoding.TypeMarker.DoubleQuotedLowercaseGuidString: - destinationBuffer[0] = (byte)'"'; - DecodeGuidStringValue(encodedString, isUpperCaseGuid: false, destinationBuffer.Slice(start: 1)); - destinationBuffer[GuidWithQuotesLength - 1] = (byte)'"'; + DecodeCompressedStringValue(numberOfBits: 7, encodedString, baseChar: 0, destinationBuffer); break; default: @@ -1465,5 +1595,168 @@ private static void DecodeGuidStringValue(ReadOnlySpan encodedString, bool SetFixedSizedValue(destinationBuffer.Slice(start: 32), byteLookupTable[encodedString[14]]); SetFixedSizedValue(destinationBuffer.Slice(start: 34), byteLookupTable[encodedString[15]]); } + + private static bool TryConvertBase64StringToBytes( + ReadOnlySpan stringValue, + bool isBase64Url, + Span destinationBuffer, + out int bytesWritten) + { + if (stringValue.IsEmpty || (stringValue.Length % 4 == 1)) + { + throw new ArgumentException("Invalid Base64 string.", nameof(stringValue)); + } + + int padding = 0; + if (stringValue[stringValue.Length - 1] == '=') + { + padding++; + if (stringValue[stringValue.Length - 2] == '=') + { + padding++; + } + } + + int unpaddedLength = stringValue.Length - padding; + int paddedLength = (unpaddedLength + 3) / 4 * 4; + + // Since the string padded length is a multiple of 4, we can write the length divided by 4 + int paddedLengthDiv4 = paddedLength / 4; + + if (paddedLengthDiv4 > 0xFFFF) + { + throw new ArgumentException("Base64 string length is too large.", nameof(stringValue)); + } + + // Compute the encoding prefix length + // TypeMarker (1 byte) + Length (1 or 2 bytes) + Padding Length (1 byte) + int prefixLength = TypeMarkerLength + (paddedLengthDiv4 <= 0xFF ? OneByteLength : TwoByteLength) + OneByteLength; + + // Compute the required buffer length + int byteCount = unpaddedLength * 3 / 4; + + // Write the value prefix + if (paddedLengthDiv4 <= 0xFF) + { + destinationBuffer[0] = isBase64Url ? TypeMarker.Base64UrlStringLength1 : TypeMarker.Base64StringLength1; + destinationBuffer[1] = (byte)paddedLengthDiv4; + } + else + { + destinationBuffer[0] = isBase64Url ? TypeMarker.Base64UrlStringLength2 : TypeMarker.Base64StringLength2; + SetFixedSizedValue(destinationBuffer.Slice(start: 1), (ushort)paddedLengthDiv4); + } + + // Write the padding length + byte requiredPadding = (byte)(paddedLength - unpaddedLength); + + // When actual padding is less than required, write the bit-wise negation of the required + // padding length to indicate that padding was omitted. + destinationBuffer[prefixLength - 1] = requiredPadding > padding ? (byte)~requiredPadding : requiredPadding; + + char[] base64Chars = new char[paddedLength]; + for (int i = 0; i < unpaddedLength; i++) + { + char c = (char)stringValue[i]; + if (isBase64Url) + { + if (c == '-') + { + c = '+'; + } + else if (c == '_') + { + c = '/'; + } + } + + base64Chars[i] = c; + } + + for (int i = unpaddedLength; i < paddedLength; i++) + { + base64Chars[i] = '='; + } + + try + { + byte[] decodedBytes = Convert.FromBase64CharArray(base64Chars, 0, base64Chars.Length); + + // Convert.FromBase64CharArray does not verify whether the character preceding the + // padding results in partial (truncated) bits. This validation must be performed manually. + if (requiredPadding > 0) + { + char c = base64Chars[unpaddedLength - 1]; + byte b = Base64StringEncodingLookupTables.StdBase64.CharToByte[c]; + byte mask = requiredPadding == 1 ? (byte)0x3 : (byte)0xF; + if ((b & mask) != 0) + { + bytesWritten = 0; + return false; + } + } + + decodedBytes.CopyTo(destinationBuffer.Slice(start: prefixLength, length: byteCount)); + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + + bytesWritten = prefixLength + byteCount; + return true; + } + + private static void ConvertBytesToBase64String( + ReadOnlySpan encodedString, + byte padding, + bool isBase64Url, + Span destinationBuffer) + { + // Adjust the padding value to reflect whether padding was omitted. + int paddedLength = (encodedString.Length + (padding > 2 ? (byte)~padding : padding)) / 3 * 4; + int valueLength = paddedLength - (padding > 2 ? (byte)~padding : 0); + + if (destinationBuffer.Length != valueLength) + { + throw new InvalidOperationException("Invalid string buffer length."); + } + + string base64String = Convert.ToBase64String(encodedString.ToArray()); + + for (int i = 0; i < valueLength; i++) + { + char c = base64String[i]; + if (isBase64Url) + { + if (c == '+') + { + c = '-'; + } + else if (c == '/') + { + c = '_'; + } + } + + destinationBuffer[i] = (byte)c; + } + } + + private static bool IsBase64StringCandidate(ReadOnlySpan stringValue) + { + // This is the maximum string length that can be encoded + if (stringValue.Length > (4 * ushort.MaxValue)) return false; + + // Accept the value as a candidate if its length is greater than or equal to the minimum + // 6-bit compressed string length, and its length modulo 4 is not equal to 1. + if (stringValue.Length >= MinCompressedStringLength6) return (stringValue.Length % 4) != 1; + + // If the length is greater than or equal to 24, accept it as a candidate only if it is a + // multiple of 4 and ends with '='. + // This primarily ensures that values like the '_rid' property are included. + return (stringValue.Length >= 24) && ((stringValue.Length % 4) == 0) && (stringValue[stringValue.Length - 1] == '='); + } } } diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.TypeMarker.cs b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.TypeMarker.cs index 0558d869e8..2b96ff2bcb 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.TypeMarker.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.TypeMarker.cs @@ -76,10 +76,26 @@ public readonly struct TypeMarker #region [0x70, 0x78): String Values (8 Values) // 0x70 - // 0x71 - // 0x72 - // 0x73 - // 0x74 + + /// + /// The type marker for a standard base64 encoded string with length encoded in 1 byte. + /// + public const byte Base64StringLength1 = 0x71; + + /// + /// The type marker for a standard base64 encoded string with length encoded in 2 bytes. + /// + public const byte Base64StringLength2 = 0x72; + + /// + /// The type marker for a URL-safe base64 encoded string with length encoded in 1 byte. + /// + public const byte Base64UrlStringLength1 = 0x73; + + /// + /// The type marker for a URL-safe base64 encoded string with length encoded in 2 bytes. + /// + public const byte Base64UrlStringLength2 = 0x74; /// /// The type marker for a guid string with only lowercase characters. @@ -87,7 +103,7 @@ public readonly struct TypeMarker public const byte LowercaseGuidString = 0x75; /// - /// The type marker for a guid string with only uppercase characaters. + /// The type marker for a guid string with only uppercase characters. /// public const byte UppercaseGuidString = 0x76; @@ -570,28 +586,37 @@ public static bool IsEncodedLengthString(byte typeMarker) } /// - /// Gets whether a typeMarker is for a compressed string. + /// Gets whether a typeMarker is for a variable length string. /// /// The input type marker. - /// Whether the typeMarker is for a compressed string. + /// Whether the typeMarker is for a variable length string. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsCompressedString(byte typeMarker) => InRange(typeMarker, CompressedLowercaseHexString, Packed7BitStringLength2 + 1); + public static bool IsVariableLengthString(byte typeMarker) + { + return IsEncodedLengthString(typeMarker) || InRange(typeMarker, StrL1, StrL4 + 1); + } /// - /// Gets whether a typeMarker is for a variable length string. + /// Gets whether a typeMarker is for a reference string. /// /// The input type marker. - /// Whether the typeMarker is for a variable length string. + /// Whether the typeMarker is for a reference string. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsVariableLengthString(byte typeMarker) => IsEncodedLengthString(typeMarker) || InRange(typeMarker, StrL1, StrL4 + 1); + public static bool IsReferenceString(byte typeMarker) + { + return InRange(typeMarker, StrR1, StrR4 + 1); + } /// - /// Gets whether a typeMarker is for a reference string. + /// Gets whether a typeMarker is for a base64-encoded string. /// /// The input type marker. - /// Whether the typeMarker is for a reference string. + /// Whether the typeMarker is for a base64-encoded string. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsReferenceString(byte typeMarker) => InRange(typeMarker, StrR1, StrR4 + 1); + public static bool IsBase64String(byte typeMarker) + { + return InRange(typeMarker, Base64StringLength1, Base64UrlStringLength2 + 1); + } /// /// Gets whether a typeMarker is for a GUID string. @@ -599,7 +624,21 @@ public static bool IsEncodedLengthString(byte typeMarker) /// The input type marker. /// Whether the typeMarker is for a GUID string. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGuidString(byte typeMarker) => InRange(typeMarker, LowercaseGuidString, DoubleQuotedLowercaseGuidString + 1); + public static bool IsGuidString(byte typeMarker) + { + return InRange(typeMarker, LowercaseGuidString, DoubleQuotedLowercaseGuidString + 1); + } + + /// + /// Gets whether a typeMarker is for a compressed string. + /// + /// The input type marker. + /// Whether the typeMarker is for a compressed string. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsCompressedString(byte typeMarker) + { + return InRange(typeMarker, CompressedLowercaseHexString, Packed7BitStringLength2 + 1); + } /// /// Gets whether a typeMarker is for a hexadecimal string. @@ -607,7 +646,10 @@ public static bool IsEncodedLengthString(byte typeMarker) /// The input type marker. /// Whether the typeMarker is for a hexadecimal string. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsHexadecimalString(byte typeMarker) => InRange(typeMarker, CompressedLowercaseHexString, CompressedUppercaseHexString + 1); + public static bool IsHexadecimalString(byte typeMarker) + { + return InRange(typeMarker, CompressedLowercaseHexString, CompressedUppercaseHexString + 1); + } /// /// Gets whether a typeMarker is for a datetime string. @@ -615,7 +657,10 @@ public static bool IsEncodedLengthString(byte typeMarker) /// The input type marker. /// Whether the typeMarker is for a datetime string. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsDateTimeString(byte typeMarker) => typeMarker == CompressedDateTimeString; + public static bool IsDateTimeString(byte typeMarker) + { + return typeMarker == CompressedDateTimeString; + } /// /// Gets whether a typeMarker is for a string. @@ -623,8 +668,11 @@ public static bool IsEncodedLengthString(byte typeMarker) /// The type maker. /// Whether the typeMarker is for a string. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsString(byte typeMarker) => InRange(typeMarker, SystemString1ByteLengthMin, UserString2ByteLengthMax) + public static bool IsString(byte typeMarker) + { + return InRange(typeMarker, SystemString1ByteLengthMin, UserString2ByteLengthMax) || InRange(typeMarker, LowercaseGuidString, StrR4 + 1); + } /// /// Gets the length of a encoded string type marker. diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.ValueLengths.cs b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.ValueLengths.cs index b755d2be5f..595d09d4b2 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.ValueLengths.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.ValueLengths.cs @@ -2,6 +2,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // ------------------------------------------------------------ +// Ignore Spelling: unpadded Json + namespace Microsoft.Azure.Cosmos.Json { using System; @@ -25,12 +27,14 @@ private static class ValueLengths private const int CS4BL1 = -10; // 4-bit Compressed string w/ 1-byte length followed by 1-byte base char private const int CS5BL1 = -11; // 5-bit Compressed string w/ 1-byte length followed by 1-byte base char private const int CS6BL1 = -12; // 6-bit Compressed string w/ 1-byte length followed by 1-byte base char - private const int Arr1 = -13; // 1-item array - private const int Obj1 = -14; // 1-property object - private const int NC1 = -15; // Fixed-size numeric items of 1-byte item count - private const int NC2 = -16; // Fixed-size numeric items of 2-byte item count - private const int ANC1 = -17; // Array of fixed-size numeric items of 1-byte item count - private const int ANC2 = -18; // Array of fixed-size numeric items of 2-byte item count + private const int B64L1 = -13; // Base64 string 1-byte encoded length + 1-byte padding length + private const int B64L2 = -14; // Base64 string 2-byte encoded length + 1-byte padding length + private const int Arr1 = -15; // 1-item array + private const int Obj1 = -16; // 1-property object + private const int NC1 = -17; // Fixed-size numeric items of 1-byte item count + private const int NC2 = -18; // Fixed-size numeric items of 2-byte item count + private const int ANC1 = -19; // Array of fixed-size numeric items of 1-byte item count + private const int ANC2 = -20; // Array of fixed-size numeric items of 2-byte item count /// /// Lookup table for encoded value length for each TypeMarker value (0 to 255) @@ -74,13 +78,13 @@ private static class ValueLengths // String Values [0x70, 0x78) 0, // 0x70 - 0, // 0x71 - 0, // 0x72 - 0, // 0x73 - 0, // 0x74 - 17, // StrGL (Lowercase GUID string) - 17, // StrGU (Uppercase GUID string) - 17, // StrGQ (Double-quoted lowercase GUID string) + B64L1, // Standard Base64-encoded string with 1-byte length and 1-byte padding length + B64L2, // Standard Base64-encoded string with 2-byte length and 1-byte padding length + B64L1, // URL-safe Base64-encoded string with 1-byte length and 1-byte padding length + B64L2, // URL-safe Base64-encoded string with 2-byte length and 1-byte padding length + 17, // Lowercase GUID string + 17, // Uppercase GUID string + 17, // Double-quoted lowercase GUID string // Compressed strings [0x78, 0x80) CS4L1, // String 1-byte length - Lowercase hexadecimal digits encoded as 4-bit characters @@ -228,6 +232,14 @@ public static long GetValueLength(ReadOnlySpan buffer) } break; + case B64L1: + length = TypeMarkerLength + OneByteLength + OneByteLength + GetBase64ByteCount(buffer[1], buffer[2]); + break; + + case B64L2: + length = TypeMarkerLength + TwoByteLength + OneByteLength + GetBase64ByteCount(MemoryMarshal.Read(buffer.Slice(1)), buffer[3]); + break; + case CS4L1: length = TypeMarkerLength + OneByteLength + GetCompressedStringLength(buffer[1], numberOfBits: 4); break; @@ -278,13 +290,52 @@ public static long GetValueLength(ReadOnlySpan buffer) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetCompressedStringLength(int length, int numberOfBits) => ((length * numberOfBits) + 7) / 8; + public static int GetCompressedStringLength(int length, int numberOfBits) + { + return ((length * numberOfBits) + 7) / 8; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetUniformNumberArrayItemSize(byte typeMarker) { return ValueLengths.Lookup[typeMarker] - 1; } + + public static byte GetBase64Padding(byte padding) + { + if (padding > 2) + { + // Indicates that padding was omitted; negate the padding value and update the value length + // to reflect this. + return (byte)~padding; + } + + return padding; + } + + public static int GetBase64ByteCount(int unpaddedLength) + { + return unpaddedLength * 3 / 4; + } + + public static int GetBase64ByteCount(int stringLengthDiv4, byte padding) + { + return GetBase64ByteCount((stringLengthDiv4 * 4) - GetBase64Padding(padding)); + } + + public static int ComputeBase64StringLength(int stringLengthDiv4, byte padding) + { + int stringLength = stringLengthDiv4 * 4; + + if (padding > 2) + { + // Indicates that padding was omitted; negate the padding value and update the value length + // to reflect this. + stringLength -= GetBase64Padding(padding); + } + + return stringLength; + } } } } diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.cs b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.cs index e8bbe4eed3..f9e3f870bb 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.cs @@ -190,20 +190,6 @@ public static int GetValueLength(ReadOnlySpan buffer) return (int)valueLength; } - /// - /// Gets the length of a particular string given its TypeMarker. - /// - /// The type marker as input - /// - /// - Non-Negative Value: The TypeMarker encodes the string length - /// - Negative Value: System or user dictionary encoded string, or encoded string length that follows The TypeMarker - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetStringLengths(byte typeMarker) - { - return JsonBinaryEncoding.StringLengths.Lengths[typeMarker]; - } - /// /// Gets the offset of the first item in an array or object /// diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonReader.JsonBinaryReader.cs b/Microsoft.Azure.Cosmos/src/Json/JsonReader.JsonBinaryReader.cs index 902a0ca7d5..82d5264185 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonReader.JsonBinaryReader.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonReader.JsonBinaryReader.cs @@ -43,34 +43,34 @@ abstract partial class JsonReader : IJsonReader JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, // String Values [0x68, 0x70) - JsonTokenType.String, // 0x68 - JsonTokenType.String, // 0x69 - JsonTokenType.String, // 0x6A - JsonTokenType.String, // 0x6B - JsonTokenType.String, // 0x6C - JsonTokenType.String, // 0x6D - JsonTokenType.String, // 0x6E - JsonTokenType.String, // 0x6F + JsonTokenType.NotStarted, // 0x68 + JsonTokenType.NotStarted, // 0x69 + JsonTokenType.NotStarted, // 0x6A + JsonTokenType.NotStarted, // 0x6B + JsonTokenType.NotStarted, // 0x6C + JsonTokenType.NotStarted, // 0x6D + JsonTokenType.NotStarted, // 0x6E + JsonTokenType.NotStarted, // 0x6F // String Values [0x70, 0x78) - JsonTokenType.String, // 0x70 - JsonTokenType.String, // 0x71 - JsonTokenType.String, // 0x72 - JsonTokenType.String, // 0x73 - JsonTokenType.String, // 0x74 - JsonTokenType.String, // StrGL (Lowercase GUID string) - JsonTokenType.String, // StrGU (Uppercase GUID string) - JsonTokenType.String, // StrGQ (Double-quoted lowercase GUID string) + JsonTokenType.NotStarted, // 0x70 + JsonTokenType.String, // Standard Base64-encoded string with 1-byte length and 1-byte padding length + JsonTokenType.String, // Standard Base64-encoded string with 2-byte length and 1-byte padding length + JsonTokenType.String, // URL-safe Base64-encoded string with 1-byte length and 1-byte padding length + JsonTokenType.String, // URL-safe Base64-encoded string with 2-byte length and 1-byte padding length + JsonTokenType.String, // Lowercase GUID string + JsonTokenType.String, // Uppercase GUID string + JsonTokenType.String, // Double-quoted lowercase GUID string // Compressed strings [0x78, 0x80) - JsonTokenType.String, // String 1-byte length - Lowercase hexadecimal digits encoded as 4-bit characters - JsonTokenType.String, // String 1-byte length - Uppercase hexadecimal digits encoded as 4-bit characters - JsonTokenType.String, // String 1-byte length - Date-time character set encoded as 4-bit characters - JsonTokenType.String, // String 1-byte Length - 4-bit packed characters relative to a base value - JsonTokenType.String, // String 1-byte Length - 5-bit packed characters relative to a base value - JsonTokenType.String, // String 1-byte Length - 6-bit packed characters relative to a base value - JsonTokenType.String, // String 1-byte Length - 7-bit packed characters - JsonTokenType.String, // String 2-byte Length - 7-bit packed characters + JsonTokenType.String, // String 1-byte length - Lowercase hexadecimal digits encoded as 4-bit characters + JsonTokenType.String, // String 1-byte length - Uppercase hexadecimal digits encoded as 4-bit characters + JsonTokenType.String, // String 1-byte length - Date-time character set encoded as 4-bit characters + JsonTokenType.String, // String 1-byte Length - 4-bit packed characters relative to a base value + JsonTokenType.String, // String 1-byte Length - 5-bit packed characters relative to a base value + JsonTokenType.String, // String 1-byte Length - 6-bit packed characters relative to a base value + JsonTokenType.String, // String 1-byte Length - 7-bit packed characters + JsonTokenType.String, // String 2-byte Length - 7-bit packed characters // TypeMarker-encoded string length (64 values) JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, JsonTokenType.String, diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonReader.JsonTextReader.cs b/Microsoft.Azure.Cosmos/src/Json/JsonReader.JsonTextReader.cs index 47a9018588..6ceefe3c27 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonReader.JsonTextReader.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonReader.JsonTextReader.cs @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.Json using System.Collections.Generic; using System.Diagnostics; using System.Linq; - using System.Text; using Microsoft.Azure.Cosmos.Core.Utf8; /// diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonWriteOptions.cs b/Microsoft.Azure.Cosmos/src/Json/JsonWriteOptions.cs index c0ec54d963..02b0d134b1 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonWriteOptions.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonWriteOptions.cs @@ -30,5 +30,10 @@ enum JsonWriteOptions /// Enables support for writing 64-bit unsigned integers (UInt64). /// EnableUInt64Values = 1 << 1, + + /// + /// Enables support for writing Base64-encoded strings. + /// + EnableBase64Strings = 1 << 2 } } diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonWriter.JsonBinaryWriter.cs b/Microsoft.Azure.Cosmos/src/Json/JsonWriter.JsonBinaryWriter.cs index 77a549fb53..22bad028f2 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonWriter.JsonBinaryWriter.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonWriter.JsonBinaryWriter.cs @@ -1,6 +1,9 @@ //------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ + +// Ignore Spelling: Json + namespace Microsoft.Azure.Cosmos.Json { using System; @@ -36,6 +39,7 @@ public static PreblittedBinaryJsonScope CapturePreblittedBinaryJsonScope(Action< initialCapacity: 256, enableNumberArrays: false, enableUint64Values: false, + enableBase64Strings: false, enableEncodedStrings: false); Contract.Requires(!jsonBinaryWriter.JsonObjectState.InArrayContext); Contract.Requires(!jsonBinaryWriter.JsonObjectState.InObjectContext); @@ -59,8 +63,14 @@ private sealed class JsonBinaryWriter : { private enum RawValueType : byte { - Token, - StrUsr, + Arr, + Arr1, + ArrArrNum, + ArrNum, + NumUI64, + Obj, + Obj1, + StrBase64, StrEncLen, StrL1, StrL2, @@ -69,13 +79,8 @@ private enum RawValueType : byte StrR2, StrR3, StrR4, - Arr1, - Obj1, - Arr, - Obj, - ArrNum, - ArrArrNum, - NumUI64, + StrUsr, + Token } private const int MaxStackAllocSize = 4 * 1024; @@ -117,13 +122,13 @@ private enum RawValueType : byte // Empty Range RawValueType.Token, // 0x70 - RawValueType.Token, // 0x71 - RawValueType.Token, // 0x72 - RawValueType.Token, // 0x73 - RawValueType.Token, // 0x74 - RawValueType.Token, // RawValueType.StrGL (Lowercase GUID string) - RawValueType.Token, // RawValueType.StrGU (Uppercase GUID string) - RawValueType.Token, // RawValueType.StrGQ (Double-quoted lowercase GUID string) + RawValueType.StrBase64, // Standard Base64-encoded string with 1-byte length and 1-byte padding length + RawValueType.StrBase64, // Standard Base64-encoded string with 2-byte length and 1-byte padding length + RawValueType.StrBase64, // URL-safe Base64-encoded string with 1-byte length and 1-byte padding length + RawValueType.StrBase64, // URL-safe Base64-encoded string with 2-byte length and 1-byte padding length + RawValueType.Token, // RawValueType.Lowercase GUID string + RawValueType.Token, // RawValueType.Uppercase GUID string + RawValueType.Token, // RawValueType.Double-quoted lowercase GUID string // Compressed strings [0x78, 0x80) RawValueType.Token, // RawValueType.String 1-byte length - Lowercase hexadecimal digits encoded as 4-bit characters @@ -254,6 +259,11 @@ private enum RawValueType : byte /// private readonly bool enableUInt64Values; + /// + /// Determines whether to enable writing of Base64-encoded strings. + /// + private readonly bool enableBase64Strings; + /// /// Writer used to write fully materialized context to the internal stream. /// @@ -296,16 +306,19 @@ private enum RawValueType : byte /// The initial capacity to avoid intermediary allocations. /// Determines whether to enable writing of uniform number arrays. /// Determines whether to enable writing of full-precision unsigned 64-bit integer values + /// Determines whether to enable writing of Base64-encoded strings. /// Determines whether to enable reference string encoding. public JsonBinaryWriter( int initialCapacity, bool enableNumberArrays, bool enableUint64Values, + bool enableBase64Strings, bool enableEncodedStrings = true, IJsonStringDictionary jsonStringDictionary = null) { this.enableNumberArrays = enableNumberArrays; this.enableUInt64Values = enableUint64Values; + this.enableBase64Strings = enableBase64Strings; this.enableEncodedStrings = enableEncodedStrings; this.binaryWriter = new JsonBinaryMemoryWriter(initialCapacity); this.bufferedContexts = new Stack(); @@ -1427,7 +1440,7 @@ private void WriteFieldNameOrString(bool isFieldName, Utf8Span utf8Span) } else if (this.enableEncodedStrings && !isFieldName - && JsonBinaryEncoding.TryEncodeCompressedString(utf8Span.Span, this.binaryWriter.Cursor, out int bytesWritten)) + && JsonBinaryEncoding.TryEncodeCompressedString(utf8Span.Span, this.binaryWriter.Cursor, this.enableBase64Strings, out int bytesWritten)) { // Encoded value as a compressed string this.binaryWriter.Position += bytesWritten; @@ -1638,6 +1651,12 @@ private void ForceRewriteRawJsonValue( { RawValueType rawType = (RawValueType)RawValueTypes[typeMarker]; + // Check for base64 string support + if (this.enableBase64Strings && (rawType == RawValueType.StrBase64)) + { + rawType = RawValueType.Token; + } + // Check for uniform number array support if (this.enableNumberArrays && ((rawType == RawValueType.ArrNum) || (rawType == RawValueType.ArrArrNum))) { @@ -1711,6 +1730,16 @@ private void ForceRewriteRawJsonValue( jsonStringDictionary); break; + case RawValueType.StrBase64: + { + Utf8String stringValue = JsonBinaryEncoding.GetUtf8StringValue( + rootBuffer, + rawJsonValue, + jsonStringDictionary: null); + this.WriteStringValue(stringValue); + } + break; + case RawValueType.Arr1: { this.JsonObjectState.RegisterToken(JsonTokenType.BeginArray); diff --git a/Microsoft.Azure.Cosmos/src/Json/JsonWriter.cs b/Microsoft.Azure.Cosmos/src/Json/JsonWriter.cs index 2fda753df2..0cfd50a852 100644 --- a/Microsoft.Azure.Cosmos/src/Json/JsonWriter.cs +++ b/Microsoft.Azure.Cosmos/src/Json/JsonWriter.cs @@ -6,7 +6,6 @@ namespace Microsoft.Azure.Cosmos.Json using System; using System.Collections.Generic; using System.Globalization; - using System.Linq; using System.Text; using Microsoft.Azure.Cosmos.Core.Utf8; using RMResources = Documents.RMResources; @@ -63,6 +62,7 @@ public static IJsonWriter Create( JsonSerializationFormat.Binary => new JsonBinaryWriter( enableNumberArrays: writeOptions.HasFlag(JsonWriteOptions.EnableNumberArrays), enableUint64Values: writeOptions.HasFlag(JsonWriteOptions.EnableUInt64Values), + enableBase64Strings: writeOptions.HasFlag(JsonWriteOptions.EnableBase64Strings), initialCapacity: initialCapacity, jsonStringDictionary: jsonStringDictionary), _ => throw new ArgumentException( diff --git a/Microsoft.Azure.Cosmos/src/Linq/BuiltinFunctions/BuiltinFunctionVisitor.cs b/Microsoft.Azure.Cosmos/src/Linq/BuiltinFunctions/BuiltinFunctionVisitor.cs index dafc1d7265..d68b1bcce0 100644 --- a/Microsoft.Azure.Cosmos/src/Linq/BuiltinFunctions/BuiltinFunctionVisitor.cs +++ b/Microsoft.Azure.Cosmos/src/Linq/BuiltinFunctions/BuiltinFunctionVisitor.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.Linq { using System; - using System.Collections.Generic; using System.Globalization; using System.Linq.Expressions; using Microsoft.Azure.Cosmos; @@ -61,6 +60,8 @@ public static SqlScalarExpression VisitBuiltinFunctionCall(MethodCallExpression case nameof(CosmosLinqExtensions.FullTextContainsAll): case nameof(CosmosLinqExtensions.FullTextContainsAny): return StringBuiltinFunctions.Visit(methodCallExpression, context); + case nameof(CosmosLinqExtensions.ArrayContainsAll): + case nameof(CosmosLinqExtensions.ArrayContainsAny): case nameof(CosmosLinqExtensions.DocumentId): case nameof(CosmosLinqExtensions.RRF): case nameof(CosmosLinqExtensions.FullTextScore): diff --git a/Microsoft.Azure.Cosmos/src/Linq/BuiltinFunctions/OtherBuiltinSystemFunctions.cs b/Microsoft.Azure.Cosmos/src/Linq/BuiltinFunctions/OtherBuiltinSystemFunctions.cs index a928f889e8..21c097be54 100644 --- a/Microsoft.Azure.Cosmos/src/Linq/BuiltinFunctions/OtherBuiltinSystemFunctions.cs +++ b/Microsoft.Azure.Cosmos/src/Linq/BuiltinFunctions/OtherBuiltinSystemFunctions.cs @@ -17,24 +17,28 @@ namespace Microsoft.Azure.Cosmos.Linq internal static class OtherBuiltinSystemFunctions { - private class RRFVisit : SqlBuiltinFunctionVisitor + private class RrfVisitor : SqlBuiltinFunctionVisitor { - public RRFVisit() + public RrfVisitor() : base("RRF", true, new List() { - new Type[]{typeof(double[])} + new Type[]{typeof(double[])}, + new Type[]{typeof(double[]), typeof(double[])} }) { } protected override SqlScalarExpression VisitImplicit(MethodCallExpression methodCallExpression, TranslationContext context) { - if (methodCallExpression.Arguments.Count == 1 - && methodCallExpression.Arguments[0] is NewArrayExpression argumentsExpressions) + if (methodCallExpression.Arguments.Count != 1 && methodCallExpression.Arguments.Count != 2) + { + throw new DocumentQueryException("Invalid Argument Count."); + } + + if (methodCallExpression.Arguments[0] is NewArrayExpression argumentsExpressions) { - // For RRF, We don't need to care about the first argument, it is the object itself and have no relevance to the computation ReadOnlyCollection functionListExpression = argumentsExpressions.Expressions; List arguments = new List(); foreach (Expression argument in functionListExpression) @@ -65,10 +69,16 @@ protected override SqlScalarExpression VisitImplicit(MethodCallExpression method arguments.Add(ExpressionToSql.VisitNonSubqueryScalarExpression(argument, context)); } + // Append the weight if exists + if (methodCallExpression.Arguments.Count == 2) + { + arguments.Add(ExpressionToSql.VisitNonSubqueryScalarExpression(methodCallExpression.Arguments[1], context)); + } + return SqlFunctionCallScalarExpression.CreateBuiltin(SqlFunctionCallScalarExpression.Names.RRF, arguments.ToImmutableArray()); } - return null; + throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, "Method {0} is not supported with the given argument list.", methodCallExpression.Method.Name)); } protected override SqlScalarExpression VisitExplicit(MethodCallExpression methodCallExpression, TranslationContext context) @@ -77,9 +87,9 @@ protected override SqlScalarExpression VisitExplicit(MethodCallExpression method } } - private class FullTextScoreVisit : SqlBuiltinFunctionVisitor + private class FullTextScoreVisitor : SqlBuiltinFunctionVisitor { - public FullTextScoreVisit() + public FullTextScoreVisitor() : base("FullTextScore", true, new List() @@ -114,9 +124,9 @@ protected override SqlScalarExpression VisitExplicit(MethodCallExpression method } } - private class VectorDistanceVisit : SqlBuiltinFunctionVisitor + private class VectorDistanceVisitor : SqlBuiltinFunctionVisitor { - public VectorDistanceVisit() + public VectorDistanceVisitor() : base("VectorDistance", true, new List() @@ -163,12 +173,74 @@ protected override SqlScalarExpression VisitExplicit(MethodCallExpression method } } + private class ArrayContainsAllAnyVisitor : SqlBuiltinFunctionVisitor + { + public ArrayContainsAllAnyVisitor(string sqlName) + : base(sqlName, + true, + new List() + { + new Type[]{typeof(object), typeof(object[])} + }) + { + } + + protected override SqlScalarExpression VisitImplicit(MethodCallExpression methodCallExpression, TranslationContext context) + { + if (methodCallExpression.Arguments.Count != 2) + { + return null; + } + + List arguments = new List + { + // First argument: the array to search in + ExpressionToSql.VisitNonSubqueryScalarExpression(methodCallExpression.Arguments[0], context) + }; + + // Unwrap the second argument based on its type + Expression secondArgument = methodCallExpression.Arguments[1]; + + switch (secondArgument) + { + case NewArrayExpression arrayExpression: + // Unwrap inline array initialization (e.g., new[] { 1, 2, 3 }) + foreach (Expression element in arrayExpression.Expressions) + { + arguments.Add(ExpressionToSql.VisitNonSubqueryScalarExpression(element, context)); + } + break; + + case ConstantExpression constantExpression when constantExpression.Value is Array constantArray: + // Unwrap constant array + foreach (object element in constantArray) + { + Expression constantElementExpression = Expression.Constant(element, element?.GetType() ?? typeof(object)); + arguments.Add(ExpressionToSql.VisitNonSubqueryScalarExpression(constantElementExpression, context)); + } + break; + + default: + return null; + } + + return SqlFunctionCallScalarExpression.CreateBuiltin(this.SqlName, arguments.ToArray()); + } + + protected override SqlScalarExpression VisitExplicit(MethodCallExpression methodCallExpression, TranslationContext context) + { + return null; + } + } + private static Dictionary FunctionsDefinitions { get; set; } static OtherBuiltinSystemFunctions() { FunctionsDefinitions = new Dictionary { + [nameof(CosmosLinqExtensions.ArrayContainsAll)] = new ArrayContainsAllAnyVisitor(sqlName: "ARRAY_CONTAINS_ALL"), + [nameof(CosmosLinqExtensions.ArrayContainsAny)] = new ArrayContainsAllAnyVisitor(sqlName: "ARRAY_CONTAINS_ANY"), [nameof(CosmosLinqExtensions.DocumentId)] = new SqlBuiltinFunctionVisitor( sqlName: "DOCUMENTID", isStatic: true, @@ -176,9 +248,9 @@ static OtherBuiltinSystemFunctions() { new Type[]{typeof(object)}, }), - [nameof(CosmosLinqExtensions.RRF)] = new RRFVisit(), - [nameof(CosmosLinqExtensions.FullTextScore)] = new FullTextScoreVisit(), - [nameof(CosmosLinqExtensions.VectorDistance)] = new VectorDistanceVisit(), + [nameof(CosmosLinqExtensions.FullTextScore)] = new FullTextScoreVisitor(), + [nameof(CosmosLinqExtensions.RRF)] = new RrfVisitor(), + [nameof(CosmosLinqExtensions.VectorDistance)] = new VectorDistanceVisitor(), }; } diff --git a/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqExtensions.cs b/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqExtensions.cs index 56f05c9faa..36e078fc72 100644 --- a/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqExtensions.cs +++ b/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqExtensions.cs @@ -48,6 +48,42 @@ public sealed class VectorDistanceOptions public int? SearchListSizeMultiplier { get; set; } } + /// + /// Returns whether all values are present in the array. + /// + /// The array object + /// values to search + /// Returns true if all values are present; otherwise, false. + /// + /// + /// root.DocumentId()); + /// ]]> + /// + /// + public static bool ArrayContainsAll(this IEnumerable obj, params object[] values) + { + throw new NotImplementedException(ClientResources.TypeCheckExtensionFunctionsNotImplemented); + } + + /// + /// Returns whether any values are present in the array. + /// + /// The array object + /// values to search + /// Returns true if any values are present; otherwise, false. + /// + /// + /// root.DocumentId()); + /// ]]> + /// + /// + public static bool ArrayContainsAny(this IEnumerable obj, params object[] values) + { + throw new NotImplementedException(ClientResources.TypeCheckExtensionFunctionsNotImplemented); + } + /// /// Returns the integer identifier corresponding to a specific item within a physical partition. /// This method is to be used in LINQ expressions only and will be evaluated on server. @@ -470,6 +506,28 @@ public static double RRF(params double[] scoringFunctions) throw new NotImplementedException(ClientResources.ExtensionMethodNotImplemented); } + /// + /// This system function is used to combine two or more scores provided by other scoring functions. + /// For more information, see https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/rrf. + /// This method is to be used in LINQ expressions only and will be evaluated on server. + /// There's no implementation provided in the client library. + /// + /// the scoring functions to combine. Valid functions are FullTextScore and VectorDistance. + /// the weights to use for scoring functions + /// Returns the the combined scores of the scoring functions. + /// + /// + /// document.RRF(document.Name.FullTextScore(), document.Address.FullTextScore())); + /// ]]> + /// + /// + public static double RRF(double[] scoringFunctions, double[] weights) + { + // The reason for not defining "this" keyword is because this causes undesirable serialization when call Expression.ToString() on this method + throw new NotImplementedException(ClientResources.ExtensionMethodNotImplemented); + } + /// /// This method generate query definition from LINQ query. /// @@ -590,6 +648,27 @@ public static FeedIterator ToStreamIterator(this IQueryable query) return linqQuery.ToStreamIterator(); } + /// + /// This extension method returns the Index Metrics for a given Response object. The index utilization metrics is to be used for debugging purposes only. + /// This result is only available if QueryRequestOptions.PopulateIndexMetrics is set to true.Returns null if the index metrics is not available in the response. + /// + /// The query Response. + /// A string represents the Index Metrics. + /// + /// This example shows a common usage of this function. + /// + /// response = await this.Container.GetLinqQueryable(requestOptions: queryRequestOption).Select(item => item.Id).CountAsync(cancellationToken); + /// string indexMetrics = response.GetIndexMetrics(); + /// ]]> + /// + /// + public static string GetIndexMetrics(this Response response) + { + return ResponseMessage.DecodeIndexMetrics(response.Headers, isBase64Encoded: false)?.Value; + } + /// /// Returns the maximum value in a generic . /// diff --git a/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqQuery.cs b/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqQuery.cs index 6676d096c9..136e0d3ba0 100644 --- a/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqQuery.cs +++ b/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqQuery.cs @@ -215,6 +215,18 @@ internal async Task> AggregateResultAsync(CancellationToken cancella { FeedResponse response = await localFeedIterator.ReadNextAsync(rootTrace, cancellationToken); headers.RequestCharge += response.RequestCharge; + + // IndexMetrics only show up on first round trip + if (response.Headers.IndexUtilizationText != null) + { + headers.IndexUtilizationText = response.Headers.IndexUtilizationText; + } + + if (response.Headers.ActivityId != null && headers.ActivityId == null) + { + headers.ActivityId = response.Headers.ActivityId; + } + result.AddRange(response); } } diff --git a/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqQueryProvider.cs b/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqQueryProvider.cs index d681caaef5..c376add39c 100644 --- a/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqQueryProvider.cs +++ b/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqQueryProvider.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.Linq { using System; - using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading; diff --git a/Microsoft.Azure.Cosmos/src/Linq/DocumentQuery.cs b/Microsoft.Azure.Cosmos/src/Linq/DocumentQuery.cs index be86284dab..3df19f75f6 100644 --- a/Microsoft.Azure.Cosmos/src/Linq/DocumentQuery.cs +++ b/Microsoft.Azure.Cosmos/src/Linq/DocumentQuery.cs @@ -15,7 +15,6 @@ namespace Microsoft.Azure.Cosmos.Linq using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Query; - using Microsoft.Azure.Cosmos.Query.Core; using Microsoft.Azure.Cosmos.Query.Core.Metrics; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; diff --git a/Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs b/Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs index 66ad64f8ed..85840d493b 100644 --- a/Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs +++ b/Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs @@ -8,16 +8,12 @@ namespace Microsoft.Azure.Cosmos.Linq using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; - using System.Data.Common; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reflection; - using System.Text.RegularExpressions; using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.Query.Core.ClientDistributionPlan.Cql; - using Microsoft.Azure.Cosmos.Serialization.HybridRow; using Microsoft.Azure.Cosmos.Serializer; using Microsoft.Azure.Cosmos.Spatial; using Microsoft.Azure.Cosmos.SqlObjects; diff --git a/Microsoft.Azure.Cosmos/src/MetadataRequestThrottleRetryPolicy.cs b/Microsoft.Azure.Cosmos/src/MetadataRequestThrottleRetryPolicy.cs index 204bcbd830..e089ce1a85 100644 --- a/Microsoft.Azure.Cosmos/src/MetadataRequestThrottleRetryPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/MetadataRequestThrottleRetryPolicy.cs @@ -53,6 +53,11 @@ internal sealed class MetadataRequestThrottleRetryPolicy : IDocumentClientRetryP /// private int unavailableEndpointRetryCount; + /// + /// The request being sent to the service. + /// + private DocumentServiceRequest request; + /// /// The constructor to initialize an instance of . /// @@ -93,6 +98,12 @@ public Task ShouldRetryAsync( { if (exception is CosmosException cosmosException) { + DefaultTrace.TraceInformation("MetadataRequestThrottleRetryPolicy: Evaluating retry for CosmosException with StatusCode: {0}, SubStatusCode: {1}, ResourceType {2}, CollectionName {3}, ResourceID {4}.", + cosmosException.StatusCode, + cosmosException.SubStatusCode, + this.request.ResourceType, + this.request.CollectionName, + this.request.ResourceId); return this.ShouldRetryInternalAsync( cosmosException.StatusCode, (SubStatusCodes)cosmosException.SubStatusCode, @@ -102,11 +113,27 @@ public Task ShouldRetryAsync( if (exception is DocumentClientException clientException) { + DefaultTrace.TraceInformation("MetadataRequestThrottleRetryPolicy: Evaluating retry for DocumentClientException with StatusCode: {0}, SubStatusCode: {1}, ResourceType {2}, CollectionName {3}, ResourceID {4}", + clientException.StatusCode, + clientException.GetSubStatus(), + this.request.ResourceType, + this.request.CollectionName, + this.request.ResourceId); return this.ShouldRetryInternalAsync( clientException.StatusCode, clientException.GetSubStatus(), exception, cancellationToken); } + else + { + DefaultTrace.TraceInformation("MetadataRequestThrottleRetryPolicy: Evaluating retry for Exception of type: {0}, Message: {1}, ResourceType {2}, CollectionName {3}, ResourceID {4}", + exception.GetType().Name, + exception.Message, + this.request.ResourceType, + this.request.CollectionName, + this.request.ResourceId); + + } return this.throttlingRetryPolicy.ShouldRetryAsync(exception, cancellationToken); } @@ -186,6 +213,7 @@ private Task ShouldRetryInternalAsync( public void OnBeforeSendRequest(DocumentServiceRequest request) { // Clear the previous location-based routing directive. + this.request = request; request.RequestContext.ClearRouteToLocation(); request.RequestContext.RouteToLocation( this.retryContext.RetryLocationIndex, @@ -193,7 +221,12 @@ public void OnBeforeSendRequest(DocumentServiceRequest request) Uri metadataLocationEndpoint = this.globalEndpointManager.ResolveServiceEndpoint(request); - DefaultTrace.TraceInformation("MetadataRequestThrottleRetryPolicy: Routing the metadata request to: {0} for operation type: {1} and resource type: {2}.", metadataLocationEndpoint, request.OperationType, request.ResourceType); + DefaultTrace.TraceInformation("MetadataRequestThrottleRetryPolicy: Routing the metadata request to: {0} for operation type: {1} and resource type: {2} for collection: {3} with collection rid {4}.", + metadataLocationEndpoint, + request.OperationType, + request.ResourceType, + request.CollectionName, + request.ResourceId); request.RequestContext.RouteToLocation(metadataLocationEndpoint); } diff --git a/Microsoft.Azure.Cosmos/src/Microsoft.Azure.Cosmos.csproj b/Microsoft.Azure.Cosmos/src/Microsoft.Azure.Cosmos.csproj index 94887160d2..474d350b44 100644 --- a/Microsoft.Azure.Cosmos/src/Microsoft.Azure.Cosmos.csproj +++ b/Microsoft.Azure.Cosmos/src/Microsoft.Azure.Cosmos.csproj @@ -39,7 +39,7 @@ false false Microsoft.Azure.Cosmos - NU5125 + NU5125;CS1572;CS1573;CS1574;CS1587;SA1000;SA1001;SA1002;SA1003;SA1008;SA1025;SA1027;SA1108;SA1122;SA1127;SA1137;SA1203;SA1310;SA1500;SA1505;SA1507;SA1518;SA1616;SA1627;SA1649;VSTHRD200 true $(LangVersion) MIT @@ -128,16 +128,17 @@ - - - + + + + diff --git a/Microsoft.Azure.Cosmos/src/Pagination/ParallelPrefetch.Testing.cs b/Microsoft.Azure.Cosmos/src/Pagination/ParallelPrefetch.Testing.cs index b37fa2f456..c60455a711 100644 --- a/Microsoft.Azure.Cosmos/src/Pagination/ParallelPrefetch.Testing.cs +++ b/Microsoft.Azure.Cosmos/src/Pagination/ParallelPrefetch.Testing.cs @@ -57,6 +57,8 @@ public int AwaitedTasks IReadOnlyDictionary ITrace.Data => this.innerTrace.Data; + bool ITrace.IsBeingWalked => this.innerTrace.IsBeingWalked; + public ParallelPrefetchTestConfig( ArrayPool prefetcherPool, ArrayPool taskPool, @@ -116,6 +118,11 @@ void IDisposable.Dispose() { this.innerTrace.Dispose(); } + + bool ITrace.TryGetDatum(string key, out object datum) + { + return this.innerTrace.TryGetDatum(key, out datum); + } } } } diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlIsOperatorScalarExpression.cs b/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlIsOperatorScalarExpression.cs index c606c6f6eb..dfe7740839 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlIsOperatorScalarExpression.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlIsOperatorScalarExpression.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.ClientDistributionPlan.Cql { using System; - using System.Collections.Generic; internal class CqlIsOperatorScalarExpression : CqlScalarExpression { diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlNumberLiteral.cs b/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlNumberLiteral.cs index e0aa1d929d..7635ee5484 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlNumberLiteral.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlNumberLiteral.cs @@ -4,8 +4,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.ClientDistributionPlan.Cql { - using System.Collections.Generic; - internal class CqlNumberLiteral : CqlLiteral { public CqlNumberLiteral(Number64 value) diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlPropertyRefScalarExpression.cs b/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlPropertyRefScalarExpression.cs index e7e2cff74d..4ffc0f731b 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlPropertyRefScalarExpression.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/ClientDistributionPlan/Cql/CqlPropertyRefScalarExpression.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.ClientDistributionPlan.Cql { using System; - using System.Collections.Generic; internal class CqlPropertyRefScalarExpression : CqlScalarExpression { diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/CompositeIndexIndexMetrics.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/CompositeIndexIndexMetrics.cs index 5ee147dc87..b6cd280f6c 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/CompositeIndexIndexMetrics.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/CompositeIndexIndexMetrics.cs @@ -3,14 +3,13 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics { - using System; using System.Collections.Generic; using Newtonsoft.Json; /// /// Query index utilization data for composite indexes (sub-structure of the Index Metrics class) in the Azure Cosmos database service. /// - #if INTERNAL +#if INTERNAL #pragma warning disable SA1600 #pragma warning disable CS1591 public diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/CompositeIndexUtilizationEntity.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/CompositeIndexUtilizationEntity.cs index 32c51c9a30..b49c353a84 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/CompositeIndexUtilizationEntity.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/CompositeIndexUtilizationEntity.cs @@ -3,7 +3,6 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics { - using System; using System.Collections.Generic; using Newtonsoft.Json; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/FetchExecutionRangeAccumulator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/FetchExecutionRangeAccumulator.cs index cf71516eda..e62803c4c9 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/FetchExecutionRangeAccumulator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/FetchExecutionRangeAccumulator.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics { using System; using System.Collections.Generic; - using System.Diagnostics; using Microsoft.Azure.Documents; /// diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricsInfo.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricsInfo.cs index 4156e520a7..6b326b54f6 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricsInfo.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricsInfo.cs @@ -3,12 +3,7 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics { - using System; - using System.Collections.Generic; - using System.Linq; using System.Text; - using Microsoft.Azure.Cosmos.Core; - using Microsoft.Azure.Cosmos.Core.Utf8; using Newtonsoft.Json; /// diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricsInfoEntity.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricsInfoEntity.cs index 69a0f3270a..4be74bb821 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricsInfoEntity.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricsInfoEntity.cs @@ -3,12 +3,8 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics { - using System; using System.Collections.Generic; using System.Linq; - using System.Text; - using Microsoft.Azure.Cosmos.Core; - using Microsoft.Azure.Cosmos.Core.Utf8; using Newtonsoft.Json; /// diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexUtilizationInfo.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexUtilizationInfo.cs index a9ef4569b5..811877c55e 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexUtilizationInfo.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexUtilizationInfo.cs @@ -6,9 +6,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics using System; using System.Collections.Generic; using System.Linq; - using System.Text; - using Microsoft.Azure.Cosmos.Core; - using Microsoft.Azure.Cosmos.Core.Utf8; using Newtonsoft.Json; /// diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SchedulingStopwatch.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SchedulingStopwatch.cs index 0b4ca878c8..ea0cdb73b6 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SchedulingStopwatch.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SchedulingStopwatch.cs @@ -3,7 +3,6 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics { - using System.Diagnostics; using Microsoft.Azure.Documents; /// diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SchedulingTimeSpan.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SchedulingTimeSpan.cs index eea25b9594..b6118fb2cf 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SchedulingTimeSpan.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SchedulingTimeSpan.cs @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics using System.Collections.Generic; using System.Globalization; using System.Linq; - using System.Text; using Newtonsoft.Json; /// diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/ServerSideMetricsInternalAccumulator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/ServerSideMetricsInternalAccumulator.cs index 261f8786e9..cce608ca0e 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/ServerSideMetricsInternalAccumulator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/ServerSideMetricsInternalAccumulator.cs @@ -6,8 +6,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics { using System; using System.Collections.Generic; - using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Cosmos.Tracing.TraceData; internal sealed class ServerSideMetricsInternalAccumulator { diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/ServerSideMetricsTraceExtractor.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/ServerSideMetricsTraceExtractor.cs index 0bfafabab9..398f886097 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/ServerSideMetricsTraceExtractor.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/ServerSideMetricsTraceExtractor.cs @@ -17,6 +17,9 @@ public static void WalkTraceTreeForQueryMetrics(ITrace currentTrace, ServerSideM return; } + // Assert that walking state is set + System.Diagnostics.Debug.Assert(currentTrace.IsBeingWalked, "SetWalkingStateRecursively should be set to true"); + foreach (object datum in currentTrace.Data.Values) { if (datum is QueryMetricsTraceDatum queryMetricsTraceDatum) @@ -41,6 +44,9 @@ private static void WalkTraceTreeForPartitionInfo(ITrace currentTrace, ServerSid return; } + // Assert that walking state is set + System.Diagnostics.Debug.Assert(currentTrace.IsBeingWalked, "SetWalkingStateRecursively should be set to true"); + foreach (Object datum in currentTrace.Data.Values) { if (datum is ClientSideRequestStatisticsTraceDatum clientSideRequestStatisticsTraceDatum) diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SingleIndexIndexMetrics.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SingleIndexIndexMetrics.cs index 2c8826c3ea..c77922e694 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SingleIndexIndexMetrics.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SingleIndexIndexMetrics.cs @@ -3,13 +3,12 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics { - using System; using Newtonsoft.Json; /// /// Query index utilization data for single indexes (sub-structure of the Index Metrics class) in the Azure Cosmos database service. /// - #if INTERNAL +#if INTERNAL #pragma warning disable SA1600 #pragma warning disable CS1591 public diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SingleIndexUtilizationEntity.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SingleIndexUtilizationEntity.cs index 13277417de..85c5943cbc 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SingleIndexUtilizationEntity.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/SingleIndexUtilizationEntity.cs @@ -3,7 +3,6 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics { - using System; using Newtonsoft.Json; /// diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Monads/TryCatch.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Monads/TryCatch.cs index d1a2e0e0bb..31c9141781 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Monads/TryCatch.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Monads/TryCatch.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Monads { using System; - using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/AggregateQueryPipelineStage.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/AggregateQueryPipelineStage.cs index 13c245c4b3..1fe3fc4fa6 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/AggregateQueryPipelineStage.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/AggregateQueryPipelineStage.cs @@ -71,7 +71,8 @@ public override async ValueTask MoveNextAsync(ITrace trace, CancellationTo double requestCharge = 0; IReadOnlyDictionary cumulativeAdditionalHeaders = default; - + + string activityId = default; while (await this.inputStage.MoveNextAsync(trace, cancellationToken)) { TryCatch tryGetPageFromSource = this.inputStage.Current; @@ -82,6 +83,12 @@ public override async ValueTask MoveNextAsync(ITrace trace, CancellationTo } QueryPage sourcePage = tryGetPageFromSource.Result; + + if (activityId == default && sourcePage.ActivityId != default) + { + // We only take the first activityId. + activityId = sourcePage.ActivityId; + } requestCharge += sourcePage.RequestCharge; @@ -110,7 +117,7 @@ public override async ValueTask MoveNextAsync(ITrace trace, CancellationTo QueryPage queryPage = new QueryPage( documents: finalResult, requestCharge: requestCharge, - activityId: default, + activityId: activityId, cosmosQueryExecutionInfo: default, distributionPlanSpec: default, disallowContinuationTokenMessage: default, diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/AverageAggregator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/AverageAggregator.cs index 5903e7ea7e..daf09545b2 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/AverageAggregator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/AverageAggregator.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Aggregate.Aggregators { using System; - using System.Collections.Generic; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.CosmosElements.Numbers; using Microsoft.Azure.Cosmos.Query.Core.Exceptions; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/CountAggregator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/CountAggregator.cs index 61137e98d3..6131e2a704 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/CountAggregator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/CountAggregator.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Aggregate.Aggregators { using System; - using System.Globalization; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.CosmosElements.Numbers; using Microsoft.Azure.Cosmos.Query.Core.Exceptions; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeListAggregator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeListAggregator.cs index 299b0e4507..3dd87ec403 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeListAggregator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeListAggregator.cs @@ -6,12 +6,8 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Aggregate.Aggregators { using System; using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; using System.Linq; - using System.Text; using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.CosmosElements.Numbers; using Microsoft.Azure.Cosmos.Query.Core.Exceptions; using Microsoft.Azure.Cosmos.Query.Core.Monads; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeSetAggregator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeSetAggregator.cs index 987cb81e19..0c30d8fab7 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeSetAggregator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeSetAggregator.cs @@ -6,12 +6,8 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Aggregate.Aggregators { using System; using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; using System.Linq; - using System.Text; using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.CosmosElements.Numbers; using Microsoft.Azure.Cosmos.Query.Core.Exceptions; using Microsoft.Azure.Cosmos.Query.Core.Monads; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MinMaxAggregator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MinMaxAggregator.cs index ccf140e3d6..bc8939f4dd 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MinMaxAggregator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MinMaxAggregator.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Aggregate.Aggregators { using System; - using System.Collections.Generic; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Query.Core.Exceptions; using Microsoft.Azure.Cosmos.Query.Core.Monads; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs index 4364e5ce01..2e38f5d49a 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs @@ -191,7 +191,7 @@ private static async Task> TryCreateCoreContextAsy // then try seeing if we can execute as a passthrough using client side only logic. // This is to short circuit the need to go to the gateway to get the query plan. if (cosmosQueryContext.QueryClient.BypassQueryParsing() - && inputParameters.PartitionKey.HasValue) + && inputParameters.PartitionKey.HasValue && containerQueryProperties.PartitionKeyDefinition.Paths.Count <= 1) { bool parsed; SqlQuery sqlQuery; @@ -301,9 +301,10 @@ private static async Task> TryCreateFromPartitione } else { - bool singleLogicalPartitionKeyQuery = (inputParameters.PartitionKey.HasValue && targetRanges.Count == 1) + bool singleLogicalPartitionKeyQuery = ((inputParameters.PartitionKey.HasValue && targetRanges.Count == 1) || ((partitionedQueryExecutionInfo.QueryRanges.Count == 1) - && partitionedQueryExecutionInfo.QueryRanges[0].IsSingleValue); + && partitionedQueryExecutionInfo.QueryRanges[0].IsSingleValue)) + && containerQueryProperties.PartitionKeyDefinition.Paths.Count <= 1; bool serverStreamingQuery = !partitionedQueryExecutionInfo.QueryInfo.HasAggregates && !partitionedQueryExecutionInfo.QueryInfo.HasDistinct && !partitionedQueryExecutionInfo.QueryInfo.HasNonStreamingOrderBy @@ -516,9 +517,9 @@ private static TryCatch TryCreatePassthroughQueryExecutionC isMinInclusive: true, isMaxInclusive: false))) .ToList(), + partitionKey: inputParameters.PartitionKey, queryPaginationOptions: new QueryExecutionOptions( pageSizeHint: inputParameters.MaxItemCount), - partitionKey: inputParameters.PartitionKey, containerQueryProperties: containerQueryProperties, maxConcurrency: inputParameters.MaxConcurrency, prefetchPolicy: PrefetchPolicy.PrefetchSinglePage, @@ -566,6 +567,7 @@ private static TryCatch TryCreateFullQueryPipeline( allRanges: allFeedRanges, isContinuationExpected: cosmosQueryContext.IsContinuationExpected, maxConcurrency: inputParameters.MaxConcurrency, + fullTextScoreScope: inputParameters.FullTextScoreScope, requestContinuationToken: inputParameters.InitialUserContinuationToken); } @@ -837,6 +839,7 @@ private InputParameters( bool enableOptimisticDirectExecution, bool isHybridSearchQueryPlanOptimizationDisabled, bool enableDistributedQueryGatewayMode, + FullTextScoreScope fullTextScoreScope, TestInjections testInjections) { this.SqlQuerySpec = sqlQuerySpec ?? throw new ArgumentNullException(nameof(sqlQuerySpec)); @@ -852,6 +855,7 @@ private InputParameters( this.EnableOptimisticDirectExecution = enableOptimisticDirectExecution; this.IsHybridSearchQueryPlanOptimizationDisabled = isHybridSearchQueryPlanOptimizationDisabled; this.EnableDistributedQueryGatewayMode = enableDistributedQueryGatewayMode; + this.FullTextScoreScope = fullTextScoreScope; this.TestInjections = testInjections; } @@ -869,6 +873,7 @@ public static InputParameters Create( bool enableOptimisticDirectExecution, bool isHybridSearchQueryPlanOptimizationDisabled, bool enableDistributedQueryGatewayMode, + FullTextScoreScope fullTextScoreScope, TestInjections testInjections) { if (sqlQuerySpec == null) @@ -908,6 +913,7 @@ public static InputParameters Create( enableOptimisticDirectExecution: enableOptimisticDirectExecution, isHybridSearchQueryPlanOptimizationDisabled: isHybridSearchQueryPlanOptimizationDisabled, enableDistributedQueryGatewayMode: enableDistributedQueryGatewayMode, + fullTextScoreScope: fullTextScoreScope, testInjections: testInjections); } @@ -925,6 +931,8 @@ public static InputParameters Create( public bool EnableOptimisticDirectExecution { get; } public bool IsHybridSearchQueryPlanOptimizationDisabled { get; } public bool EnableDistributedQueryGatewayMode { get; } + public bool UseLengthAwareRangeComparer { get; } + public FullTextScoreScope FullTextScoreScope { get; } public InputParameters WithContinuationToken(CosmosElement token) { @@ -942,6 +950,7 @@ public InputParameters WithContinuationToken(CosmosElement token) this.EnableOptimisticDirectExecution, this.IsHybridSearchQueryPlanOptimizationDisabled, this.EnableDistributedQueryGatewayMode, + this.FullTextScoreScope, this.TestInjections); } } diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/HybridSearch/HybridSearchCrossPartitionQueryPipelineStage.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/HybridSearch/HybridSearchCrossPartitionQueryPipelineStage.cs index 83caa63ff0..ad33de5d3a 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/HybridSearch/HybridSearchCrossPartitionQueryPipelineStage.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/HybridSearch/HybridSearchCrossPartitionQueryPipelineStage.cs @@ -92,7 +92,8 @@ public static TryCatch MonadicCreate( IReadOnlyList allRanges, int maxItemCount, bool isContinuationExpected, - int maxConcurrency) + int maxConcurrency, + Cosmos.FullTextScoreScope fullTextScoreScope) { TryCatch ComponentPipelineFactory(QueryInfo rewrittenQueryInfo) { @@ -124,10 +125,16 @@ TryCatch ComponentPipelineFactory(QueryInfo rewrittenQueryI queryInfo.GlobalStatisticsQuery, sqlQuerySpec.Parameters); + // When FullTextScoreScope is Global, use allRanges (all partitions) for statistics. + // When FullTextScoreScope is Local, use targetRanges (only the filtered partitions) for statistics. + IReadOnlyList statisticsTargetRanges = fullTextScoreScope == Cosmos.FullTextScoreScope.Global + ? allRanges + : targetRanges; + TryCatch tryCatchGlobalStatisticsPipeline = ParallelCrossPartitionQueryPipelineStage.MonadicCreate( documentContainer: documentContainer, sqlQuerySpec: globalStatisticsQuerySpec, - targetRanges: allRanges, + targetRanges: statisticsTargetRanges, queryPaginationOptions: queryExecutionOptions, partitionKey: null, containerQueryProperties: containerQueryProperties, @@ -653,6 +660,7 @@ private static QueryInfo RewriteOrderByQueryInfo(QueryInfo queryInfo, GlobalFull } string rewrittenQuery = FormatComponentQueryTextWorkaround(queryInfo.RewrittenQuery, statistics, componentCount); + HybridSearchDebugTraceHelpers.TraceComponentQueryText(rewrittenQuery); QueryInfo result = new QueryInfo() { @@ -954,6 +962,17 @@ private static class HybridSearchDebugTraceHelpers private const bool Enabled = false; #pragma warning disable CS0162 // Unreachable code detected + [Conditional("DEBUG")] + public static void TraceComponentQueryText(string queryText) + { + if (Enabled) + { + System.Diagnostics.Trace.WriteLine("Component Query Text:"); + System.Diagnostics.Trace.WriteLine(queryText); + System.Diagnostics.Trace.WriteLine("\n"); + } + } + [Conditional("DEBUG")] public static void TraceQuerySpec(SqlQuerySpec querySpec) { @@ -1043,27 +1062,31 @@ private static StringBuilder AppendQueryResult(HybridSearchQueryResult queryResu return builder; } + [Conditional("DEBUG")] public static void TraceQueryResultTSVHeader(int componentCount) { - StringBuilder builder = new StringBuilder(); - builder.Append("_rid"); - builder.Append("\t"); - builder.Append("Payload"); - builder.Append("\t"); - - for (int componentIndex = 0; componentIndex < componentCount; ++componentIndex) + if (Enabled) { - builder.Append($"Score{componentIndex}"); + StringBuilder builder = new StringBuilder(); + builder.Append("_rid"); + builder.Append("\t"); + builder.Append("Payload"); builder.Append("\t"); - } - builder.Append("RRFScore"); - builder.Append("\t"); + for (int componentIndex = 0; componentIndex < componentCount; ++componentIndex) + { + builder.Append($"Score{componentIndex}"); + builder.Append("\t"); + } - builder.Remove(builder.Length - 1, 1); // remove extra tab + builder.Append("RRFScore"); + builder.Append("\t"); - string header = builder.ToString(); - System.Diagnostics.Trace.WriteLine(header); + builder.Remove(builder.Length - 1, 1); // remove extra tab + + string header = builder.ToString(); + System.Diagnostics.Trace.WriteLine(header); + } } private static void TraceFullDebugTSVHeader(int componentCount) diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/HybridSearch/HybridSearchQueryResult.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/HybridSearch/HybridSearchQueryResult.cs index 07b4ee2b17..75740b3379 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/HybridSearch/HybridSearchQueryResult.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/HybridSearch/HybridSearchQueryResult.cs @@ -70,7 +70,10 @@ public static HybridSearchQueryResult Create(CosmosElement document) throw new ArgumentException($"{FieldNames.ComponentScores} must exist."); } - CosmosElement payload = outerPayloadExists ? outerPayload : CosmosUndefined.Create(); + if (!cosmosObject.TryGetValue(FieldNames.Payload, out CosmosElement payload)) + { + payload = CosmosUndefined.Create(); + } result = new HybridSearchQueryResult(rid, componentScores, payload); } diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/OrderBy/OrderByQueryPartitionRangePageAsyncEnumerator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/OrderBy/OrderByQueryPartitionRangePageAsyncEnumerator.cs index 9d49d9a78e..404dccb513 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/OrderBy/OrderByQueryPartitionRangePageAsyncEnumerator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/OrderBy/OrderByQueryPartitionRangePageAsyncEnumerator.cs @@ -158,7 +158,7 @@ public InnerEnumerator CloneWithMaxPageSize() protected override async Task> GetNextPageAsync(ITrace trace, CancellationToken cancellationToken) { - FeedRangeInternal feedRange = HierarchicalPartitionUtils.LimitFeedRangeToSinglePartition(this.PartitionKey, this.FeedRangeState.FeedRange, this.containerQueryProperties); + FeedRangeInternal feedRange = QueryRangeUtils.LimitHpkFeedRangeToPartition(this.PartitionKey, this.FeedRangeState.FeedRange, this.containerQueryProperties); TryCatch monadicQueryPage = await this.queryDataSource .MonadicQueryAsync( diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/Parallel/ParallelCrossPartitionQueryPipelineStage.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/Parallel/ParallelCrossPartitionQueryPipelineStage.cs index e5c2cf6b8b..5a2fc378d2 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/Parallel/ParallelCrossPartitionQueryPipelineStage.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/Parallel/ParallelCrossPartitionQueryPipelineStage.cs @@ -146,7 +146,7 @@ public static TryCatch MonadicCreate( if (targetRanges.Count == 0) { - throw new ArgumentException($"{nameof(targetRanges)} must have some elements"); + return TryCatch.FromResult(new EmptyQueryPipelineStage()); } TryCatch> monadicExtractState = MonadicExtractState(continuationToken, targetRanges); diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/Parallel/QueryPartitionRangePageAsyncEnumerator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/Parallel/QueryPartitionRangePageAsyncEnumerator.cs index ebb57d0781..8e918d16f7 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/Parallel/QueryPartitionRangePageAsyncEnumerator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/Parallel/QueryPartitionRangePageAsyncEnumerator.cs @@ -46,7 +46,7 @@ protected override Task> GetNextPageAsync(ITrace trace, Canc throw new ArgumentNullException(nameof(trace)); } - FeedRangeInternal feedRange = HierarchicalPartitionUtils.LimitFeedRangeToSinglePartition(this.partitionKey, this.FeedRangeState.FeedRange, this.containerQueryProperties); + FeedRangeInternal feedRange = QueryRangeUtils.LimitHpkFeedRangeToPartition(this.partitionKey, this.FeedRangeState.FeedRange, this.containerQueryProperties); return this.queryDataSource.MonadicQueryAsync( sqlQuerySpec: this.sqlQuerySpec, feedRangeState: new FeedRangeState(feedRange, this.FeedRangeState.State), diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Distinct/DistinctHash.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Distinct/DistinctHash.cs index 6fb2f01d43..b65050d900 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Distinct/DistinctHash.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Distinct/DistinctHash.cs @@ -9,7 +9,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Distinct using Microsoft.Azure.Cosmos.Core.Utf8; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.CosmosElements.Numbers; - using Microsoft.Azure.Cosmos.Json; internal static class DistinctHash { diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/DistributedQueryPipelineStage.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/DistributedQueryPipelineStage.cs index 189ac5187c..730f654b8b 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/DistributedQueryPipelineStage.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/DistributedQueryPipelineStage.cs @@ -9,11 +9,9 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.Json; using Microsoft.Azure.Cosmos.Pagination; using Microsoft.Azure.Cosmos.Query.Core.Monads; using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination; - using Microsoft.Azure.Cosmos.Query.Core.QueryClient; internal sealed class DistributedQueryPipelineStage : IQueryPipelineStage { diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/IQueryPipelineStage.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/IQueryPipelineStage.cs index 151ba5db9a..2f5651b7fa 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/IQueryPipelineStage.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/IQueryPipelineStage.cs @@ -4,7 +4,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline { - using System.Threading; using Microsoft.Azure.Cosmos.Pagination; using Microsoft.Azure.Cosmos.Query.Core.Monads; using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/MonadicCreatePipelineStage.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/MonadicCreatePipelineStage.cs index cffac556e0..a647d88ca2 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/MonadicCreatePipelineStage.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/MonadicCreatePipelineStage.cs @@ -4,7 +4,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline { - using System.Threading; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Query.Core.Monads; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Pagination/QueryExecutionOptions.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Pagination/QueryExecutionOptions.cs index 27e509665d..d7b1d285b0 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Pagination/QueryExecutionOptions.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Pagination/QueryExecutionOptions.cs @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; - using Microsoft.Azure.Cosmos.Json; using Microsoft.Azure.Cosmos.Pagination; using Microsoft.Azure.Documents; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/PipelineFactory.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/PipelineFactory.cs index b63da96b67..7c73d73bcc 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/PipelineFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/PipelineFactory.cs @@ -40,6 +40,7 @@ public static TryCatch MonadicCreate( IReadOnlyList allRanges, bool isContinuationExpected, int maxConcurrency, + FullTextScoreScope fullTextScoreScope, CosmosElement requestContinuationToken) { if (documentContainer == null) @@ -59,7 +60,7 @@ public static TryCatch MonadicCreate( if (targetRanges.Count == 0) { - throw new ArgumentException($"{nameof(targetRanges)} must not be empty."); + return TryCatch.FromResult(new EmptyQueryPipelineStage()); } if (queryInfo == null && hybridSearchQueryInfo == null) @@ -94,7 +95,7 @@ public static TryCatch MonadicCreate( requestContinuationToken: requestContinuationToken); } else - { + { MonadicCreatePipelineStage monadicCreatePipelineStage = (_) => HybridSearchCrossPartitionQueryPipelineStage.MonadicCreate( documentContainer: documentContainer, containerQueryProperties: containerQueryProperties, @@ -105,7 +106,8 @@ public static TryCatch MonadicCreate( allRanges: allRanges, maxItemCount: maxItemCount, isContinuationExpected: isContinuationExpected, - maxConcurrency: maxConcurrency); + maxConcurrency: maxConcurrency, + fullTextScoreScope: fullTextScoreScope); if (hybridSearchQueryInfo.Skip != null) { diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Skip/SkipQueryPipelineStage.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Skip/SkipQueryPipelineStage.cs index d4319a44b2..b1725a463c 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Skip/SkipQueryPipelineStage.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Skip/SkipQueryPipelineStage.cs @@ -5,12 +5,7 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Skip { using System; - using System.Collections.Generic; - using System.Text; - using System.Threading; - using System.Threading.Tasks; using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.Query.Core.ExecutionContext; using Microsoft.Azure.Cosmos.Query.Core.Monads; internal abstract partial class SkipQueryPipelineStage : QueryPipelineStageBase diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Take/TakeQueryPipelineStage.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Take/TakeQueryPipelineStage.cs index 32f8cb2eb1..63aee083cd 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Take/TakeQueryPipelineStage.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Take/TakeQueryPipelineStage.cs @@ -4,8 +4,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Take { - using System; - using System.Threading; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Query.Core.Monads; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdvice.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdvice.cs index ce7f45c003..813aa4b839 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdvice.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdvice.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.QueryAdvisor { using System.Collections.Generic; using System.Linq; - using System.Reflection; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdviceEntry.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdviceEntry.cs index a03308d72c..967df09d96 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdviceEntry.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdviceEntry.cs @@ -3,13 +3,9 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos.Query.Core.QueryAdvisor { - using System; using System.Collections.Generic; - using System.Linq; using System.Text; - using System.Text.Json; using System.Text.Json.Serialization; - using System.Xml.Linq; /// /// Query advice in the Azure Cosmos database service. diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdviceRuleDocumentation.xml b/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdviceRuleDocumentation.xml index e3afa6126c..5129d5b01a 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdviceRuleDocumentation.xml +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryAdvisor/QueryAdviceRuleDocumentation.xml @@ -11,7 +11,7 @@ --> - https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/queryadvisor/ + https://aka.ms/CosmosDB/QueryAdvisor/ [CDATA["Query uses ARRAY_CONTAINS with partial matching."]] diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/ContainerQueryProperties.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/ContainerQueryProperties.cs index 18d8c59a0b..67b9c62016 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/ContainerQueryProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/ContainerQueryProperties.cs @@ -15,13 +15,15 @@ public ContainerQueryProperties( IReadOnlyList> effectivePartitionKeyRanges, PartitionKeyDefinition partitionKeyDefinition, Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy, - Cosmos.GeospatialType geospatialType) + Cosmos.GeospatialType geospatialType, + bool useLengthAwareRangeComparer) { this.ResourceId = resourceId; this.EffectiveRangesForPartitionKey = effectivePartitionKeyRanges; this.PartitionKeyDefinition = partitionKeyDefinition; this.VectorEmbeddingPolicy = vectorEmbeddingPolicy; - this.GeospatialType = geospatialType; + this.GeospatialType = geospatialType; + this.UseLengthAwareRangeComparer = useLengthAwareRangeComparer; } public string ResourceId { get; } @@ -34,6 +36,7 @@ public ContainerQueryProperties( public Cosmos.VectorEmbeddingPolicy VectorEmbeddingPolicy { get; } - public Cosmos.GeospatialType GeospatialType { get; } + public Cosmos.GeospatialType GeospatialType { get; } + public bool UseLengthAwareRangeComparer { get; } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/CosmosQueryClient.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/CosmosQueryClient.cs index 454dd35217..f872f8dcf3 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/CosmosQueryClient.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/CosmosQueryClient.cs @@ -9,7 +9,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.QueryClient using System.Linq; using System.Threading; using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.Diagnostics; using Microsoft.Azure.Cosmos.Query.Core.Monads; using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination; using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; @@ -24,6 +23,13 @@ public abstract Task GetCachedContainerQueryProperties PartitionKey? partitionKey, ITrace trace, CancellationToken cancellationToken); + + // ISSUE-TODO-adityasa-2025/12/29 - Reduce Coupling: we should not use PartitionKeyRange as return type for this internal interface. + // PartitionKeyRange contains a lot more information (for e.g. RidPrefix, Throughput related information, LSN, parent range id etc), + // none of which is required by callers of these methods. The only information required is min & max values. + // Furthermore, the range is always min-inclusive and max-exclusive (since original PartitionKeyRange is such). + // Callers ultimately convert the returned PartitionKeyRange into a FeedRangeEpk. + // Applies to other methods below as well. /// /// Returns list of effective partition key ranges for a collection. @@ -81,7 +87,7 @@ public abstract Task ExecuteQueryPlanRequestAsync public abstract void ClearSessionTokenCache(string collectionFullName); public abstract Task> GetTargetPartitionKeyRangeByFeedRangeAsync( - string resourceLink, + string resourceLink, string collectionResourceId, Documents.PartitionKeyDefinition partitionKeyDefinition, FeedRangeInternal feedRangeInternal, diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPartitionProvider.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPartitionProvider.cs index 4251adcab3..95e95aa3d2 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPartitionProvider.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPartitionProvider.cs @@ -6,8 +6,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.QueryPlan { using System; using System.Collections.Generic; - using System.Diagnostics; - using System.Linq; using System.Runtime.InteropServices; using System.Text; using Microsoft.Azure.Cosmos.Core.Trace; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanHandler.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanHandler.cs index bc983b2f03..bcfe7cd81f 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanHandler.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanHandler.cs @@ -4,9 +4,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.QueryPlan { using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Query.Core.Monads; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs index 3557f00c07..a5a0a7b95e 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs @@ -10,7 +10,6 @@ namespace Microsoft.Azure.Cosmos.Query.Core.QueryPlan using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Query.Core.Monads; using Microsoft.Azure.Cosmos.Query.Core.QueryClient; - using Microsoft.Azure.Cosmos.Resource.CosmosExceptions; using Microsoft.Azure.Cosmos.Tracing; using OperationType = Documents.OperationType; using PartitionKeyDefinition = Documents.PartitionKeyDefinition; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryRangeUtils.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryRangeUtils.cs new file mode 100644 index 0000000000..087fb956cf --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryRangeUtils.cs @@ -0,0 +1,187 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Query.Core +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using Microsoft.Azure.Cosmos.Query.Core.QueryClient; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Documents.Routing; + + internal static class QueryRangeUtils + { + private static readonly bool IsLengthAwareComparisonEnabled = ConfigurationManager.IsLengthAwareRangeComparatorEnabled(); + + /// + /// Updates the FeedRange to limit the scope of incoming feedRange to logical partition within a single physical partition. + /// Generally speaking, a subpartitioned container can experience split partition at any level of hierarchical partition key. + /// This could cause a situation where more than one physical partition contains the data for a partial partition key. + /// Currently, enumerator instantiation does not honor physical partition boundary and allocates entire epk range which could spans across multiple physical partitions to the enumerator. + /// Since such an epk range does not exist at the container level, Service generates a GoneException. + /// This method restrics the range of each enumerator by intersecting it with physical partition range. + /// + public static FeedRangeInternal LimitHpkFeedRangeToPartition(PartitionKey? partitionKey, FeedRangeInternal feedRange, ContainerQueryProperties containerQueryProperties) + { + // We sadly need to check the partition key, since a user can set a partition key in the request options with a different continuation token. + // In the future the partition filtering and continuation information needs to be a tightly bounded contract (like cross feed range state). + if (partitionKey.HasValue) + { + // ISSUE-HACK-adityasa-3/25/2024 - We should not update the original feed range inside this class. + // Instead we should guarantee that when enumerator is instantiated it is limited to a single physical partition. + // Ultimately we should remove enumerator's dependency on PartitionKey. + if ((containerQueryProperties.PartitionKeyDefinition.Paths.Count > 1) && + (partitionKey.Value.InternalKey.Components.Count != containerQueryProperties.PartitionKeyDefinition.Paths.Count) && + (feedRange is FeedRangeEpk feedRangeEpk)) + { + if (containerQueryProperties.EffectiveRangesForPartitionKey == null || + containerQueryProperties.EffectiveRangesForPartitionKey.Count == 0) + { + throw new InvalidOperationException( + "EffectiveRangesForPartitionKey should be populated when PK is specified in request options."); + } + + foreach (Documents.Routing.Range epkForPartitionKey in containerQueryProperties.EffectiveRangesForPartitionKey) + { + if (Documents.Routing.Range.CheckOverlapping( + feedRangeEpk.Range, + epkForPartitionKey)) + { + if (!feedRangeEpk.Range.Equals(epkForPartitionKey)) + { + String overlappingMin; + bool minInclusive; + String overlappingMax; + bool maxInclusive; + + (IComparer> minComparer, IComparer> maxComparer) = RangeComparerProvider.GetComparers(containerQueryProperties.UseLengthAwareRangeComparer); + + if (minComparer.Compare( + epkForPartitionKey, + feedRangeEpk.Range) < 0) + { + overlappingMin = feedRangeEpk.Range.Min; + minInclusive = feedRangeEpk.Range.IsMinInclusive; + } + else + { + overlappingMin = epkForPartitionKey.Min; + minInclusive = epkForPartitionKey.IsMinInclusive; + } + + if (maxComparer.Compare( + epkForPartitionKey, + feedRangeEpk.Range) > 0) + { + overlappingMax = feedRangeEpk.Range.Max; + maxInclusive = feedRangeEpk.Range.IsMaxInclusive; + } + else + { + overlappingMax = epkForPartitionKey.Max; + maxInclusive = epkForPartitionKey.IsMaxInclusive; + } + + feedRange = new FeedRangeEpk( + new Documents.Routing.Range( + overlappingMin, + overlappingMax, + minInclusive, + maxInclusive)); + } + + break; + } + } + } + else + { + feedRange = new FeedRangePartitionKey(partitionKey.Value); + } + } + + return feedRange; + } + + /// + /// Limits the partition key ranges to fit within the provided EPK ranges. + /// Computes the overall min and max from the provided ranges, then trims each partition key range to fit within those boundaries. + /// + /// The list of partition key ranges to trim + /// The EPK ranges to use as boundaries + /// Whether to use length-aware range comparers + /// A list of trimmed partition key ranges that fit within the provided ranges + public static List LimitPartitionKeyRangesToProvidedRanges( + List partitionKeyRanges, + IReadOnlyList> providedRanges, + bool useLengthAwareComparer = true) + { + (IComparer> minComparer, IComparer> maxComparer) = RangeComparerProvider.GetComparers(useLengthAwareComparer); + + // Compute the overall min and max from providedRanges + string overallMin = providedRanges[0].Min; + string overallMax = providedRanges[0].Max; + + foreach (Range providedRange in providedRanges) + { + // ProvidedRanges are user input, which can be generally deserialized from a json representation of FeedRangeInternal. + // FeedRangeInternal allows min/max to be included or excluded. + // However PartitionKeyRange assumes min is inclusive and max is exclusive. + // This is also similar to backend behavior where EPK ranges are always min-inclusive and max-exclusive. + // Therefore, despite the possible customization at FeedRangeInternal level, we only support min-inclusive and max-exclusive ranges. + // Ideally this validation should be done at the public API. Since that is not present, we only assert below. + Debug.Assert(providedRange.IsMinInclusive, "QueryRangeUtils Assert!", "Only min-inclusive ranges are supported!"); + Debug.Assert(!providedRange.IsMaxInclusive, "QueryRangeUtils Assert!", "Only max-exclusive ranges are supported!"); + + if (minComparer.Compare(providedRange, CreateSingleValueRange(overallMin)) < 0) + { + overallMin = providedRange.Min; + } + + if (maxComparer.Compare(providedRange, CreateSingleValueRange(overallMax)) > 0) + { + overallMax = providedRange.Max; + } + } + + // Trim each range to fit within the overall boundaries + List trimmedRanges = new List(partitionKeyRanges.Count); + foreach (Documents.PartitionKeyRange range in partitionKeyRanges) + { + string trimmedMin = range.MinInclusive; + string trimmedMax = range.MaxExclusive; + + // Trim min: use the greater of range.Min and overallMin + if (minComparer.Compare(CreateSingleValueRange(range.MinInclusive), CreateSingleValueRange(overallMin)) < 0) + { + trimmedMin = overallMin; + } + + // Trim max: use the lesser of range.Max and overallMax + if (maxComparer.Compare(CreateSingleValueRange(range.MaxExclusive), CreateSingleValueRange(overallMax)) > 0) + { + trimmedMax = overallMax; + } + + trimmedRanges.Add( + new Documents.PartitionKeyRange + { + Id = range.Id, + MinInclusive = trimmedMin, + MaxExclusive = trimmedMax, + Parents = range.Parents + }); + } + + return trimmedRanges; + } + + private static Range CreateSingleValueRange(string singleValue) => new Range( + singleValue, + singleValue, + isMinInclusive: true, + isMaxInclusive: true); + } +} diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/SqlQueryResumeValue.cs b/Microsoft.Azure.Cosmos/src/Query/Core/SqlQueryResumeValue.cs index 8cf8dd0762..75ed70459c 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/SqlQueryResumeValue.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/SqlQueryResumeValue.cs @@ -6,13 +6,11 @@ namespace Microsoft.Azure.Cosmos.Query.Core { using System; using System.Collections.Generic; - using Microsoft.Azure.Cosmos.Core.Utf8; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.CosmosElements.Numbers; using Microsoft.Azure.Cosmos.Query.Core.Pipeline.CrossPartition.OrderBy; using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Distinct; using Newtonsoft.Json; - using Newtonsoft.Json.Linq; // Class that represents the resume value of a query. Primarily used to represent the resume value for order by query // The actual value is saved as a CosmosElement. Only native JSON types are supported. C* types are not supported. diff --git a/Microsoft.Azure.Cosmos/src/Query/v2Query/DefaultDocumentQueryExecutionContext.cs b/Microsoft.Azure.Cosmos/src/Query/v2Query/DefaultDocumentQueryExecutionContext.cs index f2e65f257e..d97e5408ec 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v2Query/DefaultDocumentQueryExecutionContext.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v2Query/DefaultDocumentQueryExecutionContext.cs @@ -12,7 +12,6 @@ namespace Microsoft.Azure.Cosmos.Query using Microsoft.Azure.Cosmos.Common; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Query.Core.Metrics; - using Microsoft.Azure.Cosmos.Query.Core.Pipeline.CrossPartition.Parallel; using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Cosmos.Tracing; diff --git a/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentFeedResponse.cs b/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentFeedResponse.cs index 2a7f8a41cb..42e7436a4e 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentFeedResponse.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentFeedResponse.cs @@ -14,7 +14,6 @@ namespace Microsoft.Azure.Cosmos using System.Linq.Expressions; using System.Reflection; using System.Text; - using Microsoft.Azure.Cosmos.Query; using Microsoft.Azure.Cosmos.Query.Core.Metrics; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; @@ -47,7 +46,7 @@ public DocumentFeedResponse() public DocumentFeedResponse(IEnumerable result) : this() { - this.inner = result != null ? result : Enumerable.Empty(); + this.inner = result ?? Enumerable.Empty(); } internal DocumentFeedResponse( diff --git a/Microsoft.Azure.Cosmos/src/Query/v3Query/CosmosQueryClientCore.cs b/Microsoft.Azure.Cosmos/src/Query/v3Query/CosmosQueryClientCore.cs index 69803e14f2..8f3c7ca366 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v3Query/CosmosQueryClientCore.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v3Query/CosmosQueryClientCore.cs @@ -85,7 +85,8 @@ public override async Task GetCachedContainerQueryProp effectivePartitionKeyRange, containerProperties.PartitionKey, containerProperties.VectorEmbeddingPolicy, - containerProperties.GeospatialConfig.GeospatialType); + containerProperties.GeospatialConfig.GeospatialType, + this.documentClient.UseLengthAwareRangeComparer); } public override async Task> TryGetPartitionedQueryExecutionInfoAsync( @@ -231,14 +232,16 @@ public override async Task> GetTargetPartitionKeyRangeBy using (ITrace childTrace = trace.StartChild("Get Overlapping Feed Ranges", TraceComponent.Routing, Tracing.TraceLevel.Info)) { IRoutingMapProvider routingMapProvider = await this.GetRoutingMapProviderAsync(); - List> ranges = await feedRangeInternal.GetEffectiveRangesAsync(routingMapProvider, collectionResourceId, partitionKeyDefinition, trace); + List> providedRanges = await feedRangeInternal.GetEffectiveRangesAsync(routingMapProvider, collectionResourceId, partitionKeyDefinition, trace); - return await this.GetTargetPartitionKeyRangesAsync( + List ranges = await this.GetTargetPartitionKeyRangesAsync( resourceLink, collectionResourceId, - ranges, + providedRanges, forceRefresh, childTrace); + + return QueryRangeUtils.LimitPartitionKeyRangesToProvidedRanges(ranges, providedRanges, this.clientContext.ClientOptions.UseLengthAwareRangeComparer); } } @@ -280,8 +283,8 @@ public override async Task> GetTargetPartitionKeyRangesA if (ranges == null) { throw new NotFoundException($"{DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture)}: GetTargetPartitionKeyRanges(collectionResourceId:{collectionResourceId}, providedRanges: {string.Join(",", providedRanges)} failed due to stale cache"); - } - + } + return ranges; } } diff --git a/Microsoft.Azure.Cosmos/src/Query/v3Query/FeedResponse.cs b/Microsoft.Azure.Cosmos/src/Query/v3Query/FeedResponse.cs index 33da048e5e..c4313f7381 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v3Query/FeedResponse.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v3Query/FeedResponse.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos { using System.Collections; using System.Collections.Generic; - using Microsoft.Azure.Documents; /// /// The user contract for the various feed responses that serialized the responses to a type. diff --git a/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryIterator.cs b/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryIterator.cs index 5891066b42..ca58f6c869 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryIterator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryIterator.cs @@ -151,6 +151,7 @@ public static QueryIterator Create( enableOptimisticDirectExecution: queryRequestOptions.EnableOptimisticDirectExecution, isHybridSearchQueryPlanOptimizationDisabled: queryRequestOptions.IsHybridSearchQueryPlanOptimizationDisabled, enableDistributedQueryGatewayMode: queryRequestOptions.EnableDistributedQueryGatewayMode && (clientContext.ClientOptions.ConnectionMode == ConnectionMode.Gateway), + fullTextScoreScope: queryRequestOptions.FullTextScoreScope, testInjections: queryRequestOptions.TestSettings); return new QueryIterator( @@ -180,7 +181,7 @@ public override async Task ReadNextAsync(ITrace trace, Cancella // If Correlated Id already exists and is different, add a new one in comma separated list // Scenario: A new iterator is created with same ContinuationToken and Trace - if (trace.Data.TryGetValue(QueryIterator.CorrelatedActivityIdKeyName, out object correlatedActivityIds)) + if (trace.TryGetDatum(QueryIterator.CorrelatedActivityIdKeyName, out object correlatedActivityIds)) { List correlatedIdList = correlatedActivityIds.ToString().Split(',').ToList(); if (!correlatedIdList.Contains(this.correlatedActivityId.ToString())) diff --git a/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryResponse.cs b/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryResponse.cs index 40886bf1f8..1e63409a1e 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryResponse.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryResponse.cs @@ -7,13 +7,10 @@ namespace Microsoft.Azure.Cosmos using System.Collections.Generic; using System.IO; using System.Net; - using System.Text; using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.Query.Core.Metrics; using Microsoft.Azure.Cosmos.Query.Core.QueryAdvisor; using Microsoft.Azure.Cosmos.Serializer; using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Documents; /// /// Represents the template class used by feed methods (enumeration operations) for the Azure Cosmos DB service. diff --git a/Microsoft.Azure.Cosmos/src/RMResources.Designer.cs b/Microsoft.Azure.Cosmos/src/RMResources.Designer.cs index 6093b9c9dd..6e81a42a52 100644 --- a/Microsoft.Azure.Cosmos/src/RMResources.Designer.cs +++ b/Microsoft.Azure.Cosmos/src/RMResources.Designer.cs @@ -46,7 +46,7 @@ internal RMResources() if (object.ReferenceEquals(resourceMan, null)) { #if COSMOSCLIENT - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Azure.Cosmos.RMResources", typeof(RMResources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Azure.Documents.RMResources", typeof(RMResources).Assembly); #else global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Azure.Documents.RMResources", typeof(RMResources).GetAssembly()); #endif @@ -249,6 +249,17 @@ internal static string BadUrl } } + /// + /// Looks up a localized string similar to Cannot offline when either the global account or one or more regional accounts are in an invalid state (i.e., not Online or Updating).. + /// + internal static string CannotOfflineDueToInvalidAccountStatus + { + get + { + return ResourceManager.GetString("CannotOfflineDueToInvalidAccountStatus", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot offline when per partition failover is enabled.. /// @@ -810,6 +821,17 @@ internal static string EnableMultipleWriteLocationsAndEnableServerlessNotSupport } } + /// + /// Looks up a localized string similar to Serverless accounts do not support partition merge. + /// + internal static string MergeNotSupportedForServerless + { + get + { + return ResourceManager.GetString("MergeNotSupportedForServerless", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot set EnableMultipleWriteLocations on an account with EnableTieredStorageV1. /// @@ -1096,17 +1118,6 @@ internal static string FeatureNotSupportedOnSubscription } } - /// - /// Looks up a localized string similar to MaterializedViews is not supported on this account type. - /// - internal static string MaterializedViewsNotSupportedOnZoneRedundantAccount - { - get - { - return ResourceManager.GetString("MaterializedViewsNotSupportedOnZoneRedundantAccount", resourceCulture); - } - } - /// /// Looks up a localized string similar to Federation {0} in region {1} is not found. /// @@ -1737,6 +1748,17 @@ internal static string InvalidOfferCRUDForServerless } } + /// + /// Looks up a localized string similar to Offer update operations are not supported for the account {0}. + /// + internal static string InvalidOfferUpdateOperation + { + get + { + return ResourceManager.GetString("InvalidOfferUpdateOperation", resourceCulture); + } + } + /// /// Looks up a localized string similar to Resource {0} is invalid for adding owner resource record. /// @@ -2649,6 +2671,17 @@ internal static string OfferTypeAndThroughputCannotBeSpecifiedBoth } } + /// + /// Looks up a localized string similar to EnableOnlineContainerCopy capability is not enabled.. + /// + internal static string OnlineContainerCopyNotEnabled + { + get + { + return ResourceManager.GetString("OnlineContainerCopyNotEnabled", resourceCulture); + } + } + /// /// Looks up a localized string similar to Offer throughput and autopilot settings cannot both be specified.. /// @@ -2913,6 +2946,28 @@ internal static string ProvisionLimit } } + /// + /// Looks up a localized string similar to Failed to hydrate RBAC cache.. + /// + internal static string RbacCacheHydrationFailed + { + get + { + return ResourceManager.GetString("RbacCacheHydrationFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to hydrate RBAC cache since the hydrator is unavailable.. + /// + internal static string RbacHydratorNotAvailable + { + get + { + return ResourceManager.GetString("RbacHydratorNotAvailable", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot authorize request since the [Action] to authorize was not found.. /// @@ -4091,6 +4146,28 @@ internal static string UnsupportedKeyType } } + /// + /// Looks up a localized string similar to The requested region '{0}' does not yet support AZ provisioning for Materialized View enablement. Please try other regions or disable zone redundancy for this region.. + /// + internal static string UnsupportedMaterializedViewAzRegion + { + get + { + return ResourceManager.GetString("UnsupportedMaterializedViewAzRegion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The requested region '{0}' is not supported for Materialized View enablement.. + /// + internal static string UnsupportedMaterializedViewRegion + { + get + { + return ResourceManager.GetString("UnsupportedMaterializedViewRegion", resourceCulture); + } + } + /// /// Looks up a localized string similar to The operation is only supported for Serverless accounts, per-partition throughput cannot be updated directly for provisioned accounts. /// @@ -4289,6 +4366,17 @@ internal static string UnsupportedAccessControlType } } + /// + /// Looks up a localized string similar to Online container copy enablement is supported only on NoSQL accounts.. + /// + internal static string UnsupportedAccountTypeForOnlineContainerCopy + { + get + { + return ResourceManager.GetString("UnsupportedAccountTypeForOnlineContainerCopy", resourceCulture); + } + } + /// /// Looks up a localized string similar to Upserts for scripts in collections with multiple partitions are not supported.. /// @@ -4885,6 +4973,17 @@ internal static string EnableDataMaskingPolicyAndEnableLogStoreNotSupported } } + /// + /// Looks up a localized string similar to All versions and deletes change feed mode should be enabled for Online Container Copy.. + /// + internal static string EnableFFCFWithPITRNotEnabledForOnlineContainerCopy + { + get + { + return ResourceManager.GetString("EnableFFCFWithPITRNotEnabledForOnlineContainerCopy", resourceCulture); + } + } + /// /// Looks up a localized string similar to Change of CapacityMode from Serverless to Provisioned is failed as account: {0} is not serverless account. /// @@ -4896,6 +4995,28 @@ internal static string NotServerlessAccount } } + /// + /// Looks up a localized string similar to NRegion Commit write barrier has not been met for the request.. + /// + internal static string NRegionCommitWriteBarrierNotMet + { + get + { + return ResourceManager.GetString("NRegionCommitWriteBarrierNotMet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Service is currently unavailable. More info: https://aka.ms/cosmosdb-tsg-service-unavailable. N-Region Commit write barrier has not been met for the request.. + /// + internal static string Server_NRegionCommitWriteBarrierNotMet + { + get + { + return ResourceManager.GetString("Server_NRegionCommitWriteBarrierNotMet", resourceCulture); + } + } + /// /// Looks up a localized string similar to DataMaskingPolicy is not supported on this account type. /// @@ -4983,5 +5104,93 @@ internal static string DisableCmkNotSupported return ResourceManager.GetString("DisableCmkNotSupported", resourceCulture); } } + + /// + /// Looks up a localized string similar to Soft Delete and Multiple Write Locations cannot be enabled on the same account. + /// + internal static string SoftDeletionNotSupportedWithMultipleWriteLocations + { + get + { + return ResourceManager.GetString("SoftDeletionNotSupportedWithMultipleWriteLocations", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Resource is soft deleted. + /// + internal static string ResourceIsSoftDeleted + { + get + { + return ResourceManager.GetString("ResourceIsSoftDeleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DisableLocalAuthDeniedBecauseOfKeysUsage if keyUri is not valid to update on an account. + /// + internal static string DisableLocalAuthDeniedBecauseOfKeysUsage + { + get + { + return ResourceManager.GetString("DisableLocalAuthDeniedBecauseOfKeysUsage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to UnexpectedErrorCheckingKeysUsageInDisableLocalAuth if keyUri is not valid to update on an account. + /// + internal static string UnexpectedErrorCheckingKeysUsageInDisableLocalAuth + { + get + { + return ResourceManager.GetString("UnexpectedErrorCheckingKeysUsageInDisableLocalAuth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to KeyRegenerationDeniedBecauseOfKeysUsage if there is recent key usage on a cdb account. + /// + internal static string KeyRegenerationDeniedBecauseOfKeysUsage + { + get + { + return ResourceManager.GetString("KeyRegenerationDeniedBecauseOfKeysUsage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to UnexpectedErrorCheckingKeysUsageInKeyRegeneration if there is recent key usage on a cdb account. + /// + internal static string UnexpectedErrorCheckingKeysUsageInKeyRegeneration + { + get + { + return ResourceManager.GetString("UnexpectedErrorCheckingKeysUsageInKeyRegeneration", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Missing Soft Delete Action Kind From Request if SoftDeleteActionKind is missing from Header or Query Parameters + /// + internal static string MissingSoftDeleteActionKindFromRequest + { + get + { + return ResourceManager.GetString("MissingSoftDeleteActionKindFromRequest", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot set SoftDeletionMetadata when disabling soft delete + /// + internal static string CannotSetSoftDeletionMetadataWhenDisablingSoftDeleteErrorMessage + { + get + { + return ResourceManager.GetString("CannotSetSoftDeletionMetadataWhenDisablingSoftDeleteErrorMessage", resourceCulture); + } + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/RMResources.resx b/Microsoft.Azure.Cosmos/src/RMResources.resx index 56ca90f30d..c76174fae8 100644 --- a/Microsoft.Azure.Cosmos/src/RMResources.resx +++ b/Microsoft.Azure.Cosmos/src/RMResources.resx @@ -861,9 +861,6 @@ Feature {0} is not supported in {1} region - - Materialized Views are not supported on account with zone redundant region - Database and Write Location are not matching @@ -1054,6 +1051,9 @@ If you would like to serve this query through continuation tokens, then please r Serverless accounts do not support multiple regions. + + Serverless accounts do not support partition merge. + "Operation '{0}' on resource '{1}' is not allowed through Azure Cosmos DB endpoint. Please switch on such operations for your account, or perform this operation through Azure Resource Manager, Azure Portal, Azure CLI or Azure Powershell" @@ -1114,9 +1114,6 @@ If you would like to serve this query through continuation tokens, then please r This could be because the user's group memberships were not present in the AAD token. - - The given request [{0} {1}] cannot be authorized by AAD token in data plane. Learn more: https://aka.ms/cosmos-native-rbac. - Failed to validate AAD identity since the authenticator is unavailable. @@ -1207,6 +1204,12 @@ If you would like to serve this query through continuation tokens, then please r Cannot authorize request since the [Action] to authorize was not found. + + Failed to hydrate RBAC cache. Please retry again later. + + + Failed to hydrate RBAC cache since the hydrator is unavailable. + Cross tenant CMK database account doesn't support using delegated identity as the default identity. @@ -1458,4 +1461,52 @@ If you would like to serve this query through continuation tokens, then please r {0} capability is not supported on subscription {1} + + Soft Delete and Multiple Write Locations cannot be enabled on the same account. + + + NRegion Commit write barrier has not been met for the request, since the gap between the local LSN and global n-region committed lsn is more than 1. + + + Offer update operations are not supported for the account {0}. + + + The requested region '{0}' is not supported for Materialized View enablement. + + + The requested region '{0}' does not yet support AZ provisioning for Materialized View enablement. Please try other regions or disable zone redundancy for this region. + + + Cannot disable local authentication: key usage detected on {0}. Stop using keys before proceeding. More info: https://aka.ms/cosmosdb-disable-local-auth + + + Unexpected error occurred while disabling local authentication. Please ensure that your account is not using keys and try again. More info: https://aka.ms/cosmosdb-disable-local-auth + + + The given request [{0} {1}] cannot be authorized by AAD token in data plane. Learn more: https://aka.ms/cosmos-native-rbac. + + + Service is currently unavailable. More info: https://aka.ms/cosmosdb-tsg-service-unavailable. N-Region Commit write barrier has not been met for the request. + + + Cannot regenerate key because usage was detected on the {0} of your account. Please stop using keys before regenerating the key. More info: https://aka.ms/cosmosdb-key-rotation + + + Unexpected error occurred while checking key usage during key regeneration. Please ensure that your account is not using keys and try again. More info: https://aka.ms/cosmosdb-key-rotation + + + Online container copy enablement is supported only on NoSQL accounts. + + + All versions and deletes change feed mode should be enabled for Online Container Copy. + + + EnableOnlineContainerCopy capability is not enabled. + + + Cannot offline when Region {0} is in an invalid state (i.e., not Online or Updating). + + + Cannot set SoftDeletionMetadata when disabling soft delete. + \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/ReadFeed/Pagination/CrossPartitionReadFeedAsyncEnumerator.cs b/Microsoft.Azure.Cosmos/src/ReadFeed/Pagination/CrossPartitionReadFeedAsyncEnumerator.cs index 2526f9e31f..913b8cb428 100644 --- a/Microsoft.Azure.Cosmos/src/ReadFeed/Pagination/CrossPartitionReadFeedAsyncEnumerator.cs +++ b/Microsoft.Azure.Cosmos/src/ReadFeed/Pagination/CrossPartitionReadFeedAsyncEnumerator.cs @@ -12,7 +12,6 @@ namespace Microsoft.Azure.Cosmos.ReadFeed.Pagination using Microsoft.Azure.Cosmos.Query.Core.Monads; using Microsoft.Azure.Cosmos.Tests.Pagination; using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Documents; internal sealed class CrossPartitionReadFeedAsyncEnumerator : ITracingAsyncEnumerator>> { diff --git a/Microsoft.Azure.Cosmos/src/ReadFeed/ReadFeedCrossFeedRangeAsyncEnumerator.cs b/Microsoft.Azure.Cosmos/src/ReadFeed/ReadFeedCrossFeedRangeAsyncEnumerator.cs index 2f6780cdc9..20a9b96c0c 100644 --- a/Microsoft.Azure.Cosmos/src/ReadFeed/ReadFeedCrossFeedRangeAsyncEnumerator.cs +++ b/Microsoft.Azure.Cosmos/src/ReadFeed/ReadFeedCrossFeedRangeAsyncEnumerator.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.Cosmos.ReadFeed { using System; - using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.CosmosElements; diff --git a/Microsoft.Azure.Cosmos/src/ReadFeed/ReadFeedPage.cs b/Microsoft.Azure.Cosmos/src/ReadFeed/ReadFeedPage.cs index 806fdf4e07..138b6ffe97 100644 --- a/Microsoft.Azure.Cosmos/src/ReadFeed/ReadFeedPage.cs +++ b/Microsoft.Azure.Cosmos/src/ReadFeed/ReadFeedPage.cs @@ -6,10 +6,9 @@ namespace Microsoft.Azure.Cosmos.ReadFeed { using System; using System.Collections.Generic; - using System.Collections.Immutable; using Microsoft.Azure.Cosmos.CosmosElements; - #if INTERNAL +#if INTERNAL #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable SA1600 // Elements should be documented #pragma warning disable SA1601 // Partial elements should be documented diff --git a/Microsoft.Azure.Cosmos/src/ReadManyHelper.cs b/Microsoft.Azure.Cosmos/src/ReadManyHelper.cs index 5e0d1ba727..5cd6f586d5 100644 --- a/Microsoft.Azure.Cosmos/src/ReadManyHelper.cs +++ b/Microsoft.Azure.Cosmos/src/ReadManyHelper.cs @@ -4,7 +4,6 @@ namespace Microsoft.Azure.Cosmos { - using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/Microsoft.Azure.Cosmos/src/ReadManyQueryHelper.cs b/Microsoft.Azure.Cosmos/src/ReadManyQueryHelper.cs index 68fc4c354a..ee969e57a4 100644 --- a/Microsoft.Azure.Cosmos/src/ReadManyQueryHelper.cs +++ b/Microsoft.Azure.Cosmos/src/ReadManyQueryHelper.cs @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos using System; using System.Collections; using System.Collections.Generic; - using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; diff --git a/Microsoft.Azure.Cosmos/src/Regions.cs b/Microsoft.Azure.Cosmos/src/Regions.cs index 2be568ec55..e368d1b32e 100644 --- a/Microsoft.Azure.Cosmos/src/Regions.cs +++ b/Microsoft.Azure.Cosmos/src/Regions.cs @@ -469,5 +469,25 @@ public static class Regions /// Name of the Azure Southeast US 5 region in the Azure Cosmos DB service. /// public const string SoutheastUS5 = "Southeast US 5"; + + /// + /// Name of the Azure Northeast US 5 region in the Azure Cosmos DB service. + /// + public const string NortheastUS5 = "Northeast US 5"; + + /// + /// Name of the Azure India South Central region in the Azure Cosmos DB service. + /// + public const string IndiaSouthCentral = "India South Central"; + + /// + /// Name of the Azure Singapore Central region in the Azure Cosmos DB service. + /// + public const string SingaporeCentral = "Singapore Central"; + + /// + /// Name of the Azure Singapore North region in the Azure Cosmos DB service. + /// + public const string SingaporeNorth = "Singapore North"; } } diff --git a/Microsoft.Azure.Cosmos/src/RequestOptions/PatchItemRequestOptions.cs b/Microsoft.Azure.Cosmos/src/RequestOptions/PatchItemRequestOptions.cs index 98b19c0a16..a7d9787f25 100644 --- a/Microsoft.Azure.Cosmos/src/RequestOptions/PatchItemRequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/RequestOptions/PatchItemRequestOptions.cs @@ -4,10 +4,6 @@ namespace Microsoft.Azure.Cosmos { - using System.Collections.Generic; - using System.Linq; - using Microsoft.Azure.Documents; - /// /// Cosmos Patch request options /// diff --git a/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs b/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs index 48a66f719d..b84ca96f27 100644 --- a/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs @@ -9,7 +9,6 @@ namespace Microsoft.Azure.Cosmos using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Query; using Microsoft.Azure.Cosmos.Query.Core; - using Microsoft.Azure.Cosmos.Query.Core.Pipeline; using Microsoft.Azure.Documents; /// @@ -210,6 +209,25 @@ public ConsistencyLevel? ConsistencyLevel /// public QueryTextMode QueryTextMode { get; set; } = QueryTextMode.None; + /// + /// Gets or sets the scope for computing BM25 statistics used by FullTextScore in hybrid search queries. + /// + /// + /// The scope for computing BM25 statistics. Defaults to . + /// + /// + /// + /// When set to , BM25 statistics (term frequency, inverse document frequency, + /// and document length) are computed across all documents in the container, including all physical and logical partitions. + /// + /// + /// When set to , statistics are computed only over the subset of documents + /// within the partition key values specified in the query request. This is useful for multi-tenant scenarios where scoring + /// should reflect statistics that are accurate for a specific tenant's dataset. + /// + /// + public FullTextScoreScope FullTextScoreScope { get; set; } = FullTextScoreScope.Global; + internal CosmosElement CosmosElementContinuationToken { get; set; } internal string StartId { get; set; } @@ -228,7 +246,7 @@ public ConsistencyLevel? ConsistencyLevel internal FeedRange FeedRange { get; set; } - internal bool IsHybridSearchQueryPlanOptimizationDisabled { get; set; } = ConfigurationManager.IsHybridSearchQueryPlanOptimizationDisabled(defaultValue: true); + internal bool IsHybridSearchQueryPlanOptimizationDisabled { get; set; } = ConfigurationManager.IsHybridSearchQueryPlanOptimizationDisabled(defaultValue: false); // This is a temporary flag to enable the distributed query gateway mode. // This flag will be removed once we have a way for the client to determine diff --git a/Microsoft.Azure.Cosmos/src/RequestOptions/RequestOptions.cs b/Microsoft.Azure.Cosmos/src/RequestOptions/RequestOptions.cs index fdb9472a84..2df6816e26 100644 --- a/Microsoft.Azure.Cosmos/src/RequestOptions/RequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/RequestOptions/RequestOptions.cs @@ -89,12 +89,7 @@ public class RequestOptions /// reduce latency and increase availability. Currently there is one type of availability strategy, parallel request hedging. /// If there is a globally enabled availability strategy, setting one in the request options will override the global one. /// -#if PREVIEW - public -#else - internal -#endif - AvailabilityStrategy AvailabilityStrategy { get; set; } + public AvailabilityStrategy AvailabilityStrategy { get; set; } /// /// Gets or sets the boolean to use effective partition key routing in the cosmos db request. diff --git a/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs b/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs index e98979eea1..f348414a9d 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs @@ -5,6 +5,7 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net.Http; @@ -34,6 +35,7 @@ internal class ClientContextCore : CosmosClientContext private readonly string userAgent; private bool isDisposed = false; + private InferenceService inferenceService = null; private ClientContextCore( CosmosClient client, @@ -86,7 +88,8 @@ internal static CosmosClientContext Create( remoteCertificateValidationCallback: ClientContextCore.SslCustomValidationCallBack(clientOptions.GetServerCertificateCustomValidationCallback()), cosmosClientTelemetryOptions: clientOptions.CosmosClientTelemetryOptions, chaosInterceptorFactory: clientOptions.ChaosInterceptorFactory, - enableAsyncCacheExceptionNoSharing: clientOptions.EnableAsyncCacheExceptionNoSharing); + enableAsyncCacheExceptionNoSharing: clientOptions.EnableAsyncCacheExceptionNoSharing, + useLengthAwareRangeComparer: clientOptions.UseLengthAwareRangeComparer); return ClientContextCore.Create( cosmosClient, @@ -467,6 +470,32 @@ await this.DocumentClient.OpenConnectionsToAllReplicasAsync( cancellationToken); } + /// + internal override async Task SemanticRerankAsync( + string rerankContext, + IEnumerable documents, + IDictionary options = null, + CancellationToken cancellationToken = default) + { + InferenceService inferenceService = this.GetOrCreateInferenceService(); + return await inferenceService.SemanticRerankAsync(rerankContext, documents, options, cancellationToken); + } + + /// + internal override InferenceService GetOrCreateInferenceService() + { + if (this.inferenceService == null) + { + // Double check locking to avoid unnecessary locks + lock (this) + { + this.inferenceService ??= new InferenceService(this.client); + } + } + + return this.inferenceService; + } + public override void Dispose() { this.Dispose(true); @@ -484,6 +513,7 @@ protected virtual void Dispose(bool disposing) { this.batchExecutorCache.Dispose(); this.DocumentClient.Dispose(); + this.inferenceService?.Dispose(); } this.isDisposed = true; @@ -532,35 +562,53 @@ private async Task RunWithDiagnosticsHelperAsync( try { TResult result = await task(trace).ConfigureAwait(false); - // Checks if OpenTelemetry is configured for this operation and either Trace or Metrics are enabled by customer + if (isOtelCompatibleOperation) { // Extracts and records telemetry data from the result of the operation. OpenTelemetryAttributes otelAttributes = openTelemetry?.GetAttributes(result); - // Records the telemetry attributes for Distributed Tracing (if enabled) and Metrics - recorder.Record(otelAttributes); - RecordMetrics(getOperationName, - this.client.Endpoint, - containerName, - databaseName, - requestOptions, - attributes: otelAttributes); + // Checks if OpenTelemetry is configured for this operation when Trace are enabled by customer + if (!this.clientOptions.CosmosClientTelemetryOptions.DisableDistributedTracing) + { + // Records the telemetry attributes for Distributed Tracing (if enabled) and Metrics + recorder.Record(otelAttributes); + } + + // Checks if OpenTelemetry is configured for this operation when Metrics are enabled by customer + if (this.clientOptions.CosmosClientTelemetryOptions.IsClientMetricsEnabled) + { + RecordMetrics(getOperationName, + this.client.Endpoint, + containerName, + databaseName, + requestOptions, + attributes: otelAttributes); + } } - + return result; } catch (Exception ex) when (TryTransformException(ex, trace, this.client, out Exception cosmosException)) { if (isOtelCompatibleOperation) { - recorder.MarkFailed(cosmosException); - RecordMetrics(getOperationName, + // Checks if OpenTelemetry is configured for this operation when Trace are enabled by customer + if (!this.clientOptions.CosmosClientTelemetryOptions.DisableDistributedTracing) + { + recorder.MarkFailed(cosmosException); + } + + // Checks if OpenTelemetry is configured for this operation when Metrics are enabled by customer + if (this.clientOptions.CosmosClientTelemetryOptions.IsClientMetricsEnabled) + { + RecordMetrics(getOperationName, gatewayEndpoint, containerName, databaseName, requestOptions, cosmosException: cosmosException); + } } throw cosmosException; // Rethrow after recording telemetry diff --git a/Microsoft.Azure.Cosmos/src/Resource/Container/Container.cs b/Microsoft.Azure.Cosmos/src/Resource/Container/Container.cs index 379ee407f3..0232eb6d78 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Container/Container.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Container/Container.cs @@ -1679,6 +1679,91 @@ public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManu string processorName, ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); +#if PREVIEW + /// + /// Rerank a list of documents using semantic reranking. + /// This method uses a semantic reranker to score and reorder the provided documents + /// based on their relevance to the given reranking context. + /// + /// The sematic reranking requests will not use the regular request flow and have it's own client. This will not use the default SDK retry policies. + /// + /// To use this feature, you must set up a Semantic Reranker resource in Azure and provide the endpoint and key via the environment variable: "AZURE_COSMOS_SEMANTIC_RERANKER_INFERENCE_ENDPOINT" + /// By default the Semantic Reranking will have a default max connection limit of 50, to change this set the enviroment variable "AZURE_COSMOS_SEMANTIC_RERANKER_INFERENCE_SERVICE_MAX_CONNECTION_LIMIT" to the desired value before creating the CosmosClient. + /// + /// The context (ex: query string) to use for reranking the documents. + /// A list of documents to be reranked + /// (Optional) The options for the semantic reranking request. + /// (Optional) representing request cancellation. + /// The reranking results, typically including the reranked documents and their scores. + /// /// + /// + /// documents = new List(); + /// FeedIterator resultSetIterator = container.GetItemQueryIterator( + /// new QueryDefinition(queryString), + /// requestOptions: new QueryRequestOptions() + /// { + /// MaxItemCount = 15, + /// }); + /// + /// while (resultSetIterator.HasMoreResults) + /// { + /// FeedResponse response = await resultSetIterator.ReadNextAsync(); + /// foreach (JsonElement item in response) + /// { + /// documents.Add(item.ToString()); + /// } + /// } + /// + /// Dictionary options = new Dictionary + /// { + /// { "return_documents", true }, + /// { "top_k", 10 }, + /// { "batch_size", 32 }, + /// { "sort", true } + /// }; + /// + /// SemanticRerankResult results = await container.SemanticRerankAsync( + /// reranking_context, + /// documents, + /// options); + /// + /// // get the best resulting document from the query + /// results.RerankScores.First().Document; + /// // or the index of the document in the original list + /// results.RerankScores.First().Index; + /// // or the reranking score + /// results.RerankScores.First().Score; + /// + /// // get the latency information from the reranking operation + /// Dictonary tokenUseageInfo = results.TokenUseage; + /// ]]> + /// + /// + public abstract Task SemanticRerankAsync( + string rerankContext, + IEnumerable documents, + IDictionary options = null, + CancellationToken cancellationToken = default); +#endif + /// /// Deletes all items in the Container with the specified value. /// Starts an asynchronous Cosmos DB background operation which deletes all items in the Container with the specified value. diff --git a/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerInlineCore.cs b/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerInlineCore.cs index 44a409eed1..cd38bada65 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerInlineCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerInlineCore.cs @@ -697,5 +697,16 @@ public override Task IsFeedRangePartOfAsync( y, cancellationToken: cancellationToken)); } + +#if PREVIEW + public override Task SemanticRerankAsync( + string rerankContext, + IEnumerable documents, + IDictionary options = null, + CancellationToken cancellationToken = default) + { + return this.ClientContext.SemanticRerankAsync(rerankContext, documents, options, cancellationToken); + } +#endif } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContext.cs b/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContext.cs index e4c2592cc4..da5a613dab 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContext.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContext.cs @@ -5,6 +5,7 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -132,6 +133,33 @@ internal abstract Task InitializeContainerUsingRntbdAsync( string containerLinkUri, CancellationToken cancellationToken); + /// + /// Rerank a list of documents using semantic reranking. + /// This method uses a semantic reranker to score and reorder the provided documents + /// based on their relevance to the given reranking context. + /// + /// The sematic reranking requests will not use the regular request flow and not use the default SDK retry policies. + /// + /// The context (ex: query string) to use for reranking the documents. + /// A list of documents to be reranked + /// (Optional) The options for the semantic reranking request. + /// (Optional) representing request cancellation. + /// The reranking results, typically including the reranked documents and their scores. + internal abstract Task SemanticRerankAsync( + string rerankContext, + IEnumerable documents, + IDictionary options = null, + CancellationToken cancellationToken = default); + + /// + /// Creates, or gets if already created, the inference service for this client + /// This will have a seperate http client that is used to make calls to the inference end point + /// + /// This method exists in the client context so the infernece service can be easily disposed when the client is disposed + /// + /// the inferenceService + internal abstract InferenceService GetOrCreateInferenceService(); + public abstract void Dispose(); } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs index 61e826f6c2..703eb0a611 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs @@ -181,7 +181,7 @@ public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(this.GetType().FullName); - stringBuilder.Append(" : "); + stringBuilder.Append(": "); this.ToStringHelper(stringBuilder); diff --git a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosOperationCanceledException.cs b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosOperationCanceledException.cs index 4ed0ac8fb4..72a0f41233 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosOperationCanceledException.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosOperationCanceledException.cs @@ -19,11 +19,13 @@ namespace Microsoft.Azure.Cosmos /// [Serializable] public class CosmosOperationCanceledException : OperationCanceledException, ICloneable - { + { + private readonly Object thisLock = new object(); private readonly OperationCanceledException originalException; - private readonly Lazy lazyMessage; - private readonly Lazy toStringMessage; - private readonly bool tokenCancellationRequested; + private readonly bool tokenCancellationRequested; + + private string lazyMessage; + private string toStringMessage; /// /// Create an instance of CosmosOperationCanceledException @@ -38,8 +40,8 @@ public CosmosOperationCanceledException( this.originalException = originalException ?? throw new ArgumentNullException(nameof(originalException)); this.Diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics)); this.tokenCancellationRequested = originalException.CancellationToken.IsCancellationRequested; - this.toStringMessage = this.CreateToStringMessage(); - this.lazyMessage = this.CreateLazyMessage(); + this.toStringMessage = null; + this.lazyMessage = null; } internal CosmosOperationCanceledException( @@ -61,8 +63,8 @@ internal CosmosOperationCanceledException( } this.Diagnostics = new CosmosTraceDiagnostics(trace); this.tokenCancellationRequested = originalException.CancellationToken.IsCancellationRequested; - this.toStringMessage = this.CreateToStringMessage(); - this.lazyMessage = this.CreateLazyMessage(); + this.toStringMessage = null; + this.lazyMessage = null; } /// @@ -75,8 +77,8 @@ protected CosmosOperationCanceledException(SerializationInfo info, StreamingCont { this.originalException = (OperationCanceledException)info.GetValue("originalException", typeof(OperationCanceledException)); this.tokenCancellationRequested = (bool)info.GetValue("tokenCancellationRequested", typeof(bool)); - this.lazyMessage = new Lazy(() => (string)info.GetValue("lazyMessage", typeof(string))); - this.toStringMessage = new Lazy(() => (string)info.GetValue("toStringMessage", typeof(string))); + this.lazyMessage = null; + this.toStringMessage = null; //Diagnostics cannot be serialized this.Diagnostics = new CosmosTraceDiagnostics(NoOpTrace.Singleton); } @@ -89,7 +91,7 @@ public override string Source } /// - public override string Message => this.lazyMessage.Value; + public override string Message => this.EnsureLazyMessage(); /// #pragma warning disable CDX1002 // DontUseExceptionStackTrace @@ -120,18 +122,42 @@ public override Exception GetBaseException() /// public override string ToString() { - return this.toStringMessage.Value; + return this.EnsureToStringMessage(skipDiagnostics: false); } - private Lazy CreateLazyMessage() - { - return new Lazy(() => $"{this.originalException.Message}{Environment.NewLine}Cancellation Token has expired: {this.tokenCancellationRequested}. Learn more at: https://aka.ms/cosmosdb-tsg-request-timeout{Environment.NewLine}CosmosDiagnostics: {this.Diagnostics}"); - } - private Lazy CreateToStringMessage() - { - return new Lazy(() => $"{this.originalException}{Environment.NewLine}Cancellation Token has expired: {this.tokenCancellationRequested}. Learn more at: https://aka.ms/cosmosdb-tsg-request-timeout{Environment.NewLine}CosmosDiagnostics: {this.Diagnostics}"); - } - + private string EnsureLazyMessage() + { + if (this.lazyMessage != null) + { + return this.lazyMessage; + } + + lock (this.thisLock) + { + return this.lazyMessage ??= + $"{this.originalException.Message}{Environment.NewLine}Cancellation Token has expired: {this.tokenCancellationRequested}. Learn more at: https://aka.ms/cosmosdb-tsg-request-timeout{Environment.NewLine}CosmosDiagnostics: {this.Diagnostics}"; + } + } + + internal string EnsureToStringMessage(bool skipDiagnostics) + { + if (this.toStringMessage != null) + { + return this.toStringMessage; + } + + lock (this.thisLock) + { + if (skipDiagnostics) + { + return this.toStringMessage ??= + $"{this.originalException}{Environment.NewLine}Cancellation Token has expired: {this.tokenCancellationRequested}. Learn more at: https://aka.ms/cosmosdb-tsg-request-timeout"; + } + return this.toStringMessage ??= + $"{this.originalException}{Environment.NewLine}Cancellation Token has expired: {this.tokenCancellationRequested}. Learn more at: https://aka.ms/cosmosdb-tsg-request-timeout{Environment.NewLine}CosmosDiagnostics: {this.Diagnostics}"; + } + } + /// /// RecordOtelAttributes /// @@ -159,8 +185,8 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont info.AddValue("originalException", this.originalException); #pragma warning restore CDX1000 // DontConvertExceptionToObject info.AddValue("tokenCancellationRequested", this.tokenCancellationRequested); - info.AddValue("lazyMessage", this.lazyMessage.Value); - info.AddValue("toStringMessage", this.toStringMessage.Value); + info.AddValue("lazyMessage", this.EnsureLazyMessage()); + info.AddValue("toStringMessage", this.EnsureToStringMessage(skipDiagnostics: false)); } /// diff --git a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorCore.cs b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorCore.cs index 091a4075e3..771087c801 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorCore.cs @@ -10,10 +10,7 @@ namespace Microsoft.Azure.Cosmos using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Json; - using Microsoft.Azure.Cosmos.Query.Core; - using Microsoft.Azure.Cosmos.Serializer; using Microsoft.Azure.Cosmos.Telemetry.OpenTelemetry; using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; diff --git a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore.cs b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore.cs index 2ad8739ee2..97ff4297e1 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore.cs @@ -7,8 +7,6 @@ namespace Microsoft.Azure.Cosmos using System; using System.Threading; using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.Telemetry.OpenTelemetry; using Microsoft.Azure.Cosmos.Tracing; internal sealed class FeedIteratorInlineCore : FeedIteratorInternal @@ -28,7 +26,6 @@ internal FeedIteratorInlineCore( this.feedIteratorInternal = feedIteratorInternal; this.clientContext = clientContext; - this.querySpec = feedIteratorInternal.querySpec; this.container = feedIteratorInternal.container; this.SetupInfoForTelemetry(feedIteratorInternal); diff --git a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore{T}.cs b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore{T}.cs index 7ff0479249..a8b0e60958 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore{T}.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore{T}.cs @@ -7,8 +7,6 @@ namespace Microsoft.Azure.Cosmos using System; using System.Threading; using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.Telemetry.OpenTelemetry; using Microsoft.Azure.Cosmos.Tracing; internal sealed class FeedIteratorInlineCore : FeedIteratorInternal diff --git a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInternal.cs b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInternal.cs index e4b53fc8f5..838f6bda7e 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInternal.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInternal.cs @@ -6,7 +6,6 @@ namespace Microsoft.Azure.Cosmos { using System.Threading; using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Tracing; /// diff --git a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInternal{T}.cs b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInternal{T}.cs index 2d26e38b67..4a5bb19c87 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInternal{T}.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInternal{T}.cs @@ -6,7 +6,6 @@ namespace Microsoft.Azure.Cosmos { using System.Threading; using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Tracing; /// diff --git a/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedItem.cs b/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedItem.cs index 417cc3b1b6..5e8ac7d438 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedItem.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedItem.cs @@ -57,21 +57,22 @@ namespace Microsoft.Azure.Cosmos class ChangeFeedItem { /// - /// The full fidelity change feed current item. + /// The current version of the item for all versions and deletes change feed mode. + /// It is always null for delete change feed operations. /// [JsonProperty(PropertyName = "current")] [JsonPropertyName("current")] public T Current { get; set; } /// - /// The full fidelity change feed metadata. + /// The item metadata for all versions and deletes change feed mode. /// [JsonProperty(PropertyName = "metadata", NullValueHandling = NullValueHandling.Ignore)] [JsonPropertyName("metadata")] public ChangeFeedMetadata Metadata { get; set; } /// - /// For delete operations, previous image is always going to be provided. The previous image on replace operations is not going to be exposed by default and requires account-level or container-level opt-in. + /// The previous version of the item for all versions and deletes change feed mode. The previous version on delete and replace operations is not exposed by default and requires container-level opt-in. Refer to https://aka.ms/cosmosdb-change-feed-deletes for more information. /// [JsonProperty(PropertyName = "previous", NullValueHandling = NullValueHandling.Ignore)] [JsonPropertyName("previous")] diff --git a/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedMetadata.cs b/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedMetadata.cs index 1dae4f1e1b..19940b30db 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedMetadata.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedMetadata.cs @@ -5,9 +5,9 @@ namespace Microsoft.Azure.Cosmos { using System; - using System.Text.Json; + using System.Collections.Generic; + using System.Net; using Microsoft.Azure.Cosmos.Resource.FullFidelity; - using Microsoft.Azure.Cosmos.Resource.FullFidelity.Converters; using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -15,44 +15,138 @@ namespace Microsoft.Azure.Cosmos /// /// The metadata of a change feed resource with is initialized to . /// - [System.Text.Json.Serialization.JsonConverter(typeof(ChangeFeedMetadataConverter))] #if PREVIEW public #else internal #endif - class ChangeFeedMetadata + class ChangeFeedMetadata { + private readonly static DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + /// /// The change's conflict resolution timestamp. /// - [JsonProperty(PropertyName = ChangeFeedMetadataFields.ConflictResolutionTimestamp, NullValueHandling = NullValueHandling.Ignore)] - [JsonConverter(typeof(UnixDateTimeConverter))] - public DateTime ConflictResolutionTimestamp { get; internal set; } + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public DateTime ConflictResolutionTimestamp => UnixEpoch.AddSeconds(this.ConflictResolutionTimestampInSeconds); + + private double conflictResolutionTimestampInSeconds; + + [System.Text.Json.Serialization.JsonInclude] + [System.Text.Json.Serialization.JsonPropertyName(ChangeFeedMetadataFields.ConflictResolutionTimestamp)] + [JsonProperty(PropertyName = ChangeFeedMetadataFields.ConflictResolutionTimestamp, Required = Required.Always)] + internal double ConflictResolutionTimestampInSeconds + { + get + { + if (this.conflictResolutionTimestampInSeconds <= 0) + { + throw new System.Text.Json.JsonException( + $"{ChangeFeedMetadataFields.ConflictResolutionTimestamp} not set."); + } + return this.conflictResolutionTimestampInSeconds; + } + set + { + if (value == 0) + { + throw new System.Text.Json.JsonException( + $"{ChangeFeedMetadataFields.ConflictResolutionTimestamp} cannot be zero."); + } + this.conflictResolutionTimestampInSeconds = value; + } + } /// /// The current change's logical sequence number. /// + [System.Text.Json.Serialization.JsonInclude] + [System.Text.Json.Serialization.JsonPropertyName(ChangeFeedMetadataFields.Lsn)] [JsonProperty(PropertyName = ChangeFeedMetadataFields.Lsn, NullValueHandling = NullValueHandling.Ignore)] public long Lsn { get; internal set; } /// /// The change's feed operation type . /// + [Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))] + [System.Text.Json.Serialization.JsonInclude] + [System.Text.Json.Serialization.JsonPropertyName(ChangeFeedMetadataFields.OperationType)] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))] [JsonProperty(PropertyName = ChangeFeedMetadataFields.OperationType, NullValueHandling = NullValueHandling.Ignore)] - [JsonConverter(typeof(StringEnumConverter))] public ChangeFeedOperationType OperationType { get; internal set; } /// /// The previous change's logical sequence number. /// + [System.Text.Json.Serialization.JsonInclude] + [System.Text.Json.Serialization.JsonPropertyName(ChangeFeedMetadataFields.PreviousImageLSN)] [JsonProperty(PropertyName = ChangeFeedMetadataFields.PreviousImageLSN, NullValueHandling = NullValueHandling.Ignore)] public long PreviousLsn { get; internal set; } /// - /// Used to distinquish explicit deletes (e.g. via DeleteItem) from deletes caused by TTL expiration (a collection may define time-to-live policy for documents). + /// Used to distinguish explicit deletes (e.g. via DeleteItem) from deletes caused by TTL expiration (a collection may define time-to-live policy for documents). /// + [System.Text.Json.Serialization.JsonInclude] + [System.Text.Json.Serialization.JsonPropertyName(ChangeFeedMetadataFields.TimeToLiveExpired)] [JsonProperty(PropertyName = ChangeFeedMetadataFields.TimeToLiveExpired, NullValueHandling = NullValueHandling.Ignore)] public bool IsTimeToLiveExpired { get; internal set; } + + /// + /// Applicable for delete operations only, otherwise null. + /// The id of the previous item version. + /// + [System.Text.Json.Serialization.JsonInclude] + [System.Text.Json.Serialization.JsonPropertyName(ChangeFeedMetadataFields.Id)] + [JsonProperty(PropertyName = ChangeFeedMetadataFields.Id, NullValueHandling = NullValueHandling.Ignore)] + public string Id { get; internal set; } + + /// + /// Applicable for delete operations only, otherwise null. + /// The partition key of the previous item version represented as a dictionary where the key is the partition key property name + /// and the value is the partition key property value. All levels of hierarchy will be present if a hierarchical partition key (HPK) is used. + /// + /// + /// + /// For single partition key containers, the dictionary will contain one entry with the partition key path name (without the leading '/') + /// as the key and the partition key value as the value. + /// + /// + /// For hierarchical partition key containers, the dictionary will contain multiple entries, one for each level of the hierarchy, + /// as defined in the container's partition key definition. + /// + /// + /// Example for a single partition key container with partition key path "/tenantId": + /// + /// { + /// "tenantId": "tenant123" + /// } + /// + /// + /// + /// Example for a hierarchical partition key container with partition key paths ["/tenantId", "/userId", "/sessionId"]: + /// + /// { + /// "tenantId": "tenant123", + /// "userId": "user456", + /// "sessionId": "session789" + /// } + /// + /// + /// + /// The partition key values can be of different types (string, number, boolean, null) depending on the document's schema. + /// For example, with partition key paths ["/category", "/priority"]: + /// + /// { + /// "category": "electronics", + /// "priority": 1 + /// } + /// + /// + /// + [System.Text.Json.Serialization.JsonInclude] + [System.Text.Json.Serialization.JsonPropertyName(ChangeFeedMetadataFields.PartitionKey)] + [JsonProperty(PropertyName = ChangeFeedMetadataFields.PartitionKey, NullValueHandling = NullValueHandling.Ignore)] + public Dictionary PartitionKey { get; internal set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedMetadataFields.cs b/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedMetadataFields.cs index db39a386a9..e0005b8c05 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedMetadataFields.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedMetadataFields.cs @@ -11,5 +11,7 @@ internal class ChangeFeedMetadataFields public const string OperationType = "operationType"; public const string PreviousImageLSN = "previousImageLSN"; public const string TimeToLiveExpired = "timeToLiveExpired"; + public const string Id = "id"; + public const string PartitionKey = "partitionKey"; } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/Converters/ChangeFeedMetadataConverter.cs b/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/Converters/ChangeFeedMetadataConverter.cs index 0b5056051a..98e4d2d25f 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/Converters/ChangeFeedMetadataConverter.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FullFidelity/Converters/ChangeFeedMetadataConverter.cs @@ -42,7 +42,7 @@ public override ChangeFeedMetadata Read(ref Utf8JsonReader reader, Type typeToCo } else if (property.NameEquals(ChangeFeedMetadataFields.ConflictResolutionTimestamp)) { - metadata.ConflictResolutionTimestamp = ChangeFeedMetadataConverter.ToDateTimeFromUnixTimeInSeconds(property.Value.GetInt64()); + metadata.ConflictResolutionTimestampInSeconds = property.Value.GetInt64(); } else if (property.NameEquals(ChangeFeedMetadataFields.OperationType)) { diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountProperties.cs index 9157964b9e..d0c80c6700 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountProperties.cs @@ -7,8 +7,6 @@ namespace Microsoft.Azure.Cosmos using System; using System.Collections.Generic; using System.Collections.ObjectModel; - using System.Text; - using Microsoft.Azure.Cosmos.Telemetry; using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -269,5 +267,7 @@ private IDictionary QueryStringToDictConverter() [JsonExtensionData] internal IDictionary AdditionalProperties { get; set; } + [JsonProperty(PropertyName = Constants.Properties.EnableNRegionSynchronousCommit, NullValueHandling = NullValueHandling.Ignore)] + internal bool EnableNRegionSynchronousCommit { get; set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/FullTextScoreScope.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/FullTextScoreScope.cs new file mode 100644 index 0000000000..b068d7e2f9 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/FullTextScoreScope.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + /// + /// Specifies the scope for computing BM25 statistics used by FullTextScore in hybrid search queries. + /// + public enum FullTextScoreScope + { + /// + /// Compute BM25 statistics (term frequency, inverse document frequency, and document length) + /// across all documents in the container, including all physical and logical partitions. + /// This is the default behavior. + /// + Global, + + /// + /// Compute BM25 statistics only over the subset of documents within the partition key values + /// specified in the query. This is useful for multi-tenant scenarios where scoring should + /// reflect statistics that are accurate for a specific tenant's dataset. + /// + Local + } +} diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs index acc18cf140..7587f88732 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs @@ -591,6 +591,114 @@ public int GetHashCode(IndexingPolicy indexingPolicy) return hashCode; } } + + internal sealed class VectorIndexPathEqualityComparer : IEqualityComparer + { + public static readonly VectorIndexPathEqualityComparer Singleton = new VectorIndexPathEqualityComparer(); + + public bool Equals(VectorIndexPath vectorIndexPath1, VectorIndexPath vectorIndexPath2) + { + if (Object.ReferenceEquals(vectorIndexPath1, vectorIndexPath2)) + { + return true; + } + + if (vectorIndexPath1 == null || vectorIndexPath2 == null) + { + return false; + } + + if (vectorIndexPath1.Path != vectorIndexPath2.Path || + vectorIndexPath1.Type != vectorIndexPath2.Type || + vectorIndexPath1.QuantizerType != vectorIndexPath2.QuantizerType || + vectorIndexPath1.QuantizationByteSize != vectorIndexPath2.QuantizationByteSize || + vectorIndexPath1.IndexingSearchListSize != vectorIndexPath2.IndexingSearchListSize || + !vectorIndexPath1.AdditionalProperties.EqualsTo(vectorIndexPath2.AdditionalProperties)) + { + return false; + } + + // Compare VectorIndexShardKey arrays + if (vectorIndexPath1.VectorIndexShardKey == null && vectorIndexPath2.VectorIndexShardKey == null) + { + return true; + } + + if (vectorIndexPath1.VectorIndexShardKey == null || vectorIndexPath2.VectorIndexShardKey == null) + { + return false; + } + + if (vectorIndexPath1.VectorIndexShardKey.Length != vectorIndexPath2.VectorIndexShardKey.Length) + { + return false; + } + + HashSet shardKeys1 = new HashSet(vectorIndexPath1.VectorIndexShardKey); + HashSet shardKeys2 = new HashSet(vectorIndexPath2.VectorIndexShardKey); + + return shardKeys1.SetEquals(shardKeys2); + } + + public int GetHashCode(VectorIndexPath vectorIndexPath) + { + if (vectorIndexPath == null) + { + return 0; + } + + int hashCode = 0; + hashCode ^= vectorIndexPath.Path?.GetHashCode() ?? 0; + hashCode ^= vectorIndexPath.Type.GetHashCode(); + hashCode ^= vectorIndexPath.QuantizerType?.GetHashCode() ?? 0; + hashCode ^= vectorIndexPath.QuantizationByteSize.GetHashCode(); + hashCode ^= vectorIndexPath.IndexingSearchListSize.GetHashCode(); + + if (vectorIndexPath.VectorIndexShardKey != null) + { + foreach (string shardKey in vectorIndexPath.VectorIndexShardKey) + { + hashCode ^= shardKey?.GetHashCode() ?? 0; + } + } + + return hashCode; + } + } + + internal sealed class VectorIndexesEqualityComparer : IEqualityComparer> + { + private static readonly VectorIndexPathEqualityComparer vectorIndexPathEqualityComparer = new VectorIndexPathEqualityComparer(); + + public bool Equals(Collection vectorIndexes1, Collection vectorIndexes2) + { + if (Object.ReferenceEquals(vectorIndexes1, vectorIndexes2)) + { + return true; + } + + if (vectorIndexes1 == null || vectorIndexes2 == null) + { + return false; + } + + HashSet hashedVectorIndexes1 = new HashSet(vectorIndexes1, vectorIndexPathEqualityComparer); + HashSet hashedVectorIndexes2 = new HashSet(vectorIndexes2, vectorIndexPathEqualityComparer); + + return hashedVectorIndexes1.SetEquals(hashedVectorIndexes2); + } + + public int GetHashCode(Collection vectorIndexes) + { + int hashCode = 0; + foreach (VectorIndexPath vectorIndexPath in vectorIndexes) + { + hashCode ^= vectorIndexPathEqualityComparer.GetHashCode(vectorIndexPath); + } + + return hashCode; + } + } #endregion } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/PermissionProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/PermissionProperties.cs index 669f9a1d4a..4f454564d3 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/PermissionProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/PermissionProperties.cs @@ -6,7 +6,6 @@ namespace Microsoft.Azure.Cosmos { using System; using System.Collections.Generic; - using System.Text; using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Converters; diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/QuantizerType.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/QuantizerType.cs new file mode 100644 index 0000000000..416fbbaa91 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/QuantizerType.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos +{ + using System.Runtime.Serialization; + using Newtonsoft.Json; + using Newtonsoft.Json.Converters; + + /// + /// Defines the quantizer type of a vector index path specification in the Azure Cosmos DB service. + /// + [JsonConverter(typeof(StringEnumConverter))] +#if PREVIEW + public +#else + internal +#endif + enum QuantizerType + { + /// + /// Represents a product quantizer type. + /// + [EnumMember(Value = "product")] + Product, + + /// + /// Represents a spherical quantizer type. + /// + [EnumMember(Value = "spherical")] + Spherical + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorDataType.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorDataType.cs index 5e7ee13670..dbb63662c9 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorDataType.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorDataType.cs @@ -26,6 +26,12 @@ public enum VectorDataType /// Represent a int8 data type. /// [EnumMember(Value = "int8")] - Int8 + Int8, + + /// + /// Represent a float16 data type. + /// + [EnumMember(Value = "float16")] + Float16 } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorEmbeddingPolicy.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorEmbeddingPolicy.cs index 68dbdc6d7b..83ca8939dd 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorEmbeddingPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorEmbeddingPolicy.cs @@ -3,7 +3,6 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos { - using System; using System.Collections.Generic; using System.Collections.ObjectModel; using Newtonsoft.Json; diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorIndexPath.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorIndexPath.cs index 84907bec46..6e0f2f845b 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorIndexPath.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorIndexPath.cs @@ -39,6 +39,7 @@ namespace Microsoft.Azure.Cosmos /// { /// "path": "/embeddings/vector", /// "type": "DiskANN", + /// "quantizerType": "product", // or "spherical" /// "quantizationByteSize": 2, /// "indexingSearchListSize": 100, /// "vectorIndexShardKey": ["/Country"] @@ -68,6 +69,20 @@ public sealed class VectorIndexPath [JsonConverter(typeof(StringEnumConverter))] public VectorIndexType Type { get; set; } + /// + /// Gets or sets the quantizer type for the vector index path. This is only applicable for the quantizedFlat and diskann vector index types. + /// Allowed values are "product" and "spherical". + /// + [JsonProperty(PropertyName = Constants.Properties.QuantizerType, NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] +#if PREVIEW + public +#else + internal +#endif + QuantizerType? QuantizerType + { get; set; } + /// /// Gets or sets the quantization byte size for the vector index path. This is only applicable for the quantizedFlat and diskann vector index types. /// The allowed range for this parameter is between 1 and min(dimensions, 512). @@ -91,12 +106,7 @@ int QuantizationByteSize /// The allowed range for this parameter is between 25 and 500. /// [JsonIgnore] -#if PREVIEW - public -#else - internal -#endif - int IndexingSearchListSize + public int IndexingSearchListSize { get => this.indexingSearchListSizeInternal == null ? 0 : this.indexingSearchListSizeInternal.Value; set => this.indexingSearchListSizeInternal = value; @@ -106,12 +116,7 @@ int IndexingSearchListSize /// Gets or sets the vector index shard key for the vector index path. This is only applicable for the quantizedFlat and diskann vector index types. /// [JsonProperty(PropertyName = "vectorIndexShardKey", NullValueHandling = NullValueHandling.Ignore)] -#if PREVIEW - public -#else - internal -#endif - string[] VectorIndexShardKey { get; set; } + public string[] VectorIndexShardKey { get; set; } /// /// This contains additional values for scenarios where the SDK is not aware of new fields. diff --git a/Microsoft.Azure.Cosmos/src/RetryPolicy.cs b/Microsoft.Azure.Cosmos/src/RetryPolicy.cs index c3c829f9e2..f66841dd17 100644 --- a/Microsoft.Azure.Cosmos/src/RetryPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/RetryPolicy.cs @@ -43,8 +43,7 @@ public IDocumentClientRetryPolicy GetRequestPolicy() this.globalEndpointManager, this.partitionKeyRangeLocationCache, this.retryOptions, - this.enableEndpointDiscovery, - this.isPartitionLevelFailoverEnabled, + this.enableEndpointDiscovery, this.isThinClientEnabled); return clientRetryPolicy; diff --git a/Microsoft.Azure.Cosmos/src/Routing/AvailabilityStrategy/AvailabilityStrategy.cs b/Microsoft.Azure.Cosmos/src/Routing/AvailabilityStrategy/AvailabilityStrategy.cs index 5760533564..9e229815c9 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/AvailabilityStrategy/AvailabilityStrategy.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/AvailabilityStrategy/AvailabilityStrategy.cs @@ -46,5 +46,26 @@ public static AvailabilityStrategy CrossRegionHedgingStrategy( { return new CrossRegionHedgingAvailabilityStrategy(threshold, thresholdStep, enableMultiWriteRegionHedge); } + + /// + /// After a request's duration passes a threshold, this strategy will send out + /// hedged request to other regions. The first hedge request will be sent after the threshold. + /// After that, the strategy will send out a request every thresholdStep + /// until the request is completed or regions are exausted + /// + /// how long before SDK begins hedging + /// Period of time between first hedge and next hedging attempts + /// Whether hedging for write requests on accounts with multi-region writes are enabled + /// Note that this does come with the caveat that there will be more 409 / 412 errors thrown by the SDK. + /// This is expected and applications that adopt this feature should be prepared to handle these exceptions. + /// Application might not be able to be deterministic on Create vs Replace in the case of Upsert Operations + /// the cross region hedging availability + internal static AvailabilityStrategy SDKDefaultCrossRegionHedgingStrategyForPPAF( + TimeSpan threshold, + TimeSpan? thresholdStep, + bool enableMultiWriteRegionHedge = false) + { + return new CrossRegionHedgingAvailabilityStrategy(threshold, thresholdStep, enableMultiWriteRegionHedge, isSDKDefaultStrategy: true); + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Routing/AvailabilityStrategy/CrossRegionHedgingAvailabilityStrategy.cs b/Microsoft.Azure.Cosmos/src/Routing/AvailabilityStrategy/CrossRegionHedgingAvailabilityStrategy.cs index 6a1d7c0910..d315b330c8 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/AvailabilityStrategy/CrossRegionHedgingAvailabilityStrategy.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/AvailabilityStrategy/CrossRegionHedgingAvailabilityStrategy.cs @@ -25,6 +25,7 @@ internal class CrossRegionHedgingAvailabilityStrategy : AvailabilityStrategyInte { private const string HedgeContext = "Hedge Context"; private const string HedgeConfig = "Hedge Config"; + private const string ResponseRegion = "Response Region"; /// /// Latency threshold which activates the first region hedging @@ -44,6 +45,12 @@ internal class CrossRegionHedgingAvailabilityStrategy : AvailabilityStrategyInte /// public bool EnableMultiWriteRegionHedge { get; private set; } + /// + /// Internal flag to indicate if this is the default strategy used by the SDK when enabling + /// PPAF for clients without customer defined availability strategy. + /// + public bool IsSDKDefaultStrategyForPPAF { get; private set; } + private readonly string HedgeConfigText; /// @@ -52,10 +59,12 @@ internal class CrossRegionHedgingAvailabilityStrategy : AvailabilityStrategyInte /// /// /// + /// public CrossRegionHedgingAvailabilityStrategy( TimeSpan threshold, TimeSpan? thresholdStep, - bool enableMultiWriteRegionHedge = false) + bool enableMultiWriteRegionHedge = false, + bool isSDKDefaultStrategy = false) { if (threshold <= TimeSpan.Zero) { @@ -70,6 +79,7 @@ public CrossRegionHedgingAvailabilityStrategy( this.Threshold = threshold; this.ThresholdStep = thresholdStep ?? TimeSpan.FromMilliseconds(-1); this.EnableMultiWriteRegionHedge = enableMultiWriteRegionHedge; + this.IsSDKDefaultStrategyForPPAF = isSDKDefaultStrategy; this.HedgeConfigText = $"t:{this.Threshold.TotalMilliseconds}ms, s:{this.ThresholdStep.TotalMilliseconds}ms, w:{this.EnableMultiWriteRegionHedge}"; } @@ -197,6 +207,10 @@ internal override async Task ExecuteAvailabilityStrategyAsync( ((CosmosTraceDiagnostics)hedgeResponse.ResponseMessage.Diagnostics).Value.AddOrUpdateDatum( HedgeContext, hedgeRegions.Take(requestNumber + 1)); + // Note that the target region can be seperate than the actual region that serviced the request depending on the scenario + ((CosmosTraceDiagnostics)hedgeResponse.ResponseMessage.Diagnostics).Value.AddOrUpdateDatum( + ResponseRegion, + hedgeResponse.TargetRegionName); return hedgeResponse.ResponseMessage; } } @@ -225,6 +239,9 @@ internal override async Task ExecuteAvailabilityStrategyAsync( ((CosmosTraceDiagnostics)hedgeResponse.ResponseMessage.Diagnostics).Value.AddOrUpdateDatum( HedgeContext, hedgeRegions); + ((CosmosTraceDiagnostics)hedgeResponse.ResponseMessage.Diagnostics).Value.AddOrUpdateDatum( + ResponseRegion, + hedgeResponse.TargetRegionName); return hedgeResponse.ResponseMessage; } } @@ -269,6 +286,7 @@ private async Task CloneAndSendAsync( return await this.RequestSenderAndResultCheckAsync( sender, clonedRequest, + hedgeRegions.ElementAt(requestNumber), cancellationToken, cancellationTokenSource, trace); @@ -278,6 +296,7 @@ private async Task CloneAndSendAsync( private async Task RequestSenderAndResultCheckAsync( Func> sender, RequestMessage request, + string targetRegionName, CancellationToken cancellationToken, CancellationTokenSource cancellationTokenSource, ITrace trace) @@ -292,10 +311,10 @@ private async Task RequestSenderAndResultCheckAsync( cancellationTokenSource.Cancel(); } - return new HedgingResponse(true, response); + return new HedgingResponse(true, response, targetRegionName); } - return new HedgingResponse(false, response); + return new HedgingResponse(false, response, targetRegionName); } catch (OperationCanceledException oce) when (cancellationTokenSource.IsCancellationRequested) { @@ -337,11 +356,13 @@ private sealed class HedgingResponse { public readonly bool IsNonTransient; public readonly ResponseMessage ResponseMessage; + public readonly string TargetRegionName; - public HedgingResponse(bool isNonTransient, ResponseMessage responseMessage) + public HedgingResponse(bool isNonTransient, ResponseMessage responseMessage, string targetRegionName) { this.IsNonTransient = isNonTransient; this.ResponseMessage = responseMessage; + this.TargetRegionName = targetRegionName; } } } diff --git a/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs b/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs index 995e9020ba..cc8b5a1bb6 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs @@ -156,16 +156,35 @@ private async Task ResolveCollectionWithSessionContainerCle { string previouslyResolvedCollectionRid = request?.RequestContext?.ResolvedCollectionRid; - ContainerProperties properties = await resolveContainerProvider(); - - if (this.sessionContainer != null && - previouslyResolvedCollectionRid != null && - previouslyResolvedCollectionRid != properties.ResourceId) + try { - this.sessionContainer.ClearTokenByResourceId(previouslyResolvedCollectionRid); + ContainerProperties properties = await resolveContainerProvider(); + + if (this.sessionContainer != null && + previouslyResolvedCollectionRid != null && + previouslyResolvedCollectionRid != properties.ResourceId) + { + this.sessionContainer.ClearTokenByResourceId(previouslyResolvedCollectionRid); + } + + return properties; } + catch (DocumentClientException ex) + { + // When collection resolution fails with 404, set the appropriate substatus code. + // - For document/item operations: Set substatus 1003 (OwnerResourceNotFound) to distinguish + // "container doesn't exist" from "item doesn't exist" (substatus 0). + // - For container operations: Leave substatus as 0 because the container itself is the + // resource that doesn't exist (not an "owner" resource issue). + if (ex.StatusCode == System.Net.HttpStatusCode.NotFound && + ex.GetSubStatus() == SubStatusCodes.Unknown && + request?.ResourceType != ResourceType.Collection) + { + ex.Headers[WFConstants.BackendHeaders.SubStatus] = ((uint)SubStatusCodes.OwnerResourceNotFound).ToString(System.Globalization.CultureInfo.InvariantCulture); + } - return properties; + throw; + } } private async Task ReadCollectionAsync( @@ -244,4 +263,4 @@ await this.storeModel.ProcessMessageAsync(request)) } } } -} \ No newline at end of file +} diff --git a/Microsoft.Azure.Cosmos/src/Routing/CollectionRoutingMap.cs b/Microsoft.Azure.Cosmos/src/Routing/CollectionRoutingMap.cs index ef387dd61d..12e7f1c57c 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/CollectionRoutingMap.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/CollectionRoutingMap.cs @@ -32,24 +32,14 @@ internal sealed class CollectionRoutingMap internal int HighestNonOfflinePkRangeId { get; private set; } - public CollectionRoutingMap( - CollectionRoutingMap collectionRoutingMap, - string changeFeedNextIfNoneMatch) - { - this.rangeById = new Dictionary>(collectionRoutingMap.rangeById); - this.orderedPartitionKeyRanges = new List(collectionRoutingMap.orderedPartitionKeyRanges); - this.orderedRanges = new List>(collectionRoutingMap.orderedRanges); - this.goneRanges = new HashSet(collectionRoutingMap.goneRanges); - this.HighestNonOfflinePkRangeId = collectionRoutingMap.HighestNonOfflinePkRangeId; - this.CollectionUniqueId = collectionRoutingMap.CollectionUniqueId; - this.ChangeFeedNextIfNoneMatch = changeFeedNextIfNoneMatch; - } + private readonly (IComparer> MinComparer, IComparer> MaxComparer) comparers; private CollectionRoutingMap( Dictionary> rangeById, List orderedPartitionKeyRanges, string collectionUniqueId, - string changeFeedNextIfNoneMatch) + string changeFeedNextIfNoneMatch, + bool useLengthAwareRangeComparer) { this.rangeById = rangeById; this.orderedPartitionKeyRanges = orderedPartitionKeyRanges; @@ -82,11 +72,13 @@ private CollectionRoutingMap( } return range.Status == PartitionKeyRangeStatus.Offline ? CollectionRoutingMap.InvalidPkRangeId : pkId; }); + this.comparers = RangeComparerProvider.GetComparers(useLengthAwareRangeComparer); } public static CollectionRoutingMap TryCreateCompleteRoutingMap( IEnumerable> ranges, string collectionUniqueId, + bool useLengthAwareRangeComparer, string changeFeedNextIfNoneMatch = null) { Dictionary> rangeById = @@ -106,7 +98,7 @@ public static CollectionRoutingMap TryCreateCompleteRoutingMap( return null; } - return new CollectionRoutingMap(rangeById, orderedRanges, collectionUniqueId, changeFeedNextIfNoneMatch); + return new CollectionRoutingMap(rangeById, orderedRanges, collectionUniqueId, changeFeedNextIfNoneMatch, useLengthAwareRangeComparer); } public string CollectionUniqueId { get; private set; } @@ -142,13 +134,13 @@ public IReadOnlyList GetOverlappingRanges(IReadOnlyList providedRange in providedPartitionKeyRanges) { - int minIndex = this.orderedRanges.BinarySearch(providedRange, Range.MinComparer.Instance); + int minIndex = this.orderedRanges.BinarySearch(providedRange, this.comparers.MinComparer); if (minIndex < 0) { minIndex = Math.Max(0, (~minIndex) - 1); } - int maxIndex = this.orderedRanges.BinarySearch(providedRange, Range.MaxComparer.Instance); + int maxIndex = this.orderedRanges.BinarySearch(providedRange, this.comparers.MaxComparer); if (maxIndex < 0) { maxIndex = Math.Min(this.OrderedPartitionKeyRanges.Count - 1, ~maxIndex); @@ -216,7 +208,8 @@ public ServiceIdentity TryGetInfoByPartitionKeyRangeId(string partitionKeyRangeI public CollectionRoutingMap TryCombine( IEnumerable> ranges, - string changeFeedNextIfNoneMatch) + string changeFeedNextIfNoneMatch, + bool useLengthAwareComparer) { HashSet newGoneRanges = new HashSet(ranges.SelectMany(tuple => tuple.Item1.Parents ?? Enumerable.Empty())); newGoneRanges.UnionWith(this.goneRanges); @@ -243,7 +236,7 @@ public CollectionRoutingMap TryCombine( return null; } - return new CollectionRoutingMap(newRangeById, newOrderedRanges, this.CollectionUniqueId, changeFeedNextIfNoneMatch); + return new CollectionRoutingMap(newRangeById, newOrderedRanges, this.CollectionUniqueId, changeFeedNextIfNoneMatch, useLengthAwareComparer); } private class MinPartitionKeyTupleComparer : IComparer> diff --git a/Microsoft.Azure.Cosmos/src/Routing/GlobalEndpointManager.cs b/Microsoft.Azure.Cosmos/src/Routing/GlobalEndpointManager.cs index a76455d618..7d58e1e1f0 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/GlobalEndpointManager.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/GlobalEndpointManager.cs @@ -14,7 +14,7 @@ namespace Microsoft.Azure.Cosmos.Routing using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Common; - using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Documents; using Newtonsoft.Json.Linq; @@ -41,7 +41,12 @@ internal class GlobalEndpointManager : IGlobalEndpointManager private readonly object isAccountRefreshInProgressLock = new object(); private bool isAccountRefreshInProgress = false; private bool isBackgroundAccountRefreshActive = false; - private DateTime LastBackgroundRefreshUtc = DateTime.MinValue; + private DateTime LastBackgroundRefreshUtc = DateTime.MinValue; + + /// + /// Event that is raised when PPAF (Per Partition Automatic Failover) enablement status changes + /// + internal event Action? OnEnablePartitionLevelFailoverConfigChanged; public GlobalEndpointManager( IDocumentClientInternal owner, @@ -762,7 +767,14 @@ private async Task RefreshDatabaseAccountInternalAsync(bool forceRefresh) try { this.LastBackgroundRefreshUtc = DateTime.UtcNow; - AccountProperties accountProperties = await this.GetDatabaseAccountAsync(true); + AccountProperties accountProperties = await this.GetDatabaseAccountAsync(true); + + if (!this.connectionPolicy.DisablePartitionLevelFailoverClientLevelOverride + && accountProperties.EnablePartitionLevelFailover.HasValue + && (this.connectionPolicy.EnablePartitionLevelFailover != accountProperties.EnablePartitionLevelFailover.Value)) + { + this.OnEnablePartitionLevelFailoverConfigChanged?.Invoke(accountProperties.EnablePartitionLevelFailover.Value); + } GlobalEndpointManager.ParseThinClientLocationsFromAdditionalProperties(accountProperties); @@ -782,7 +794,8 @@ private async Task RefreshDatabaseAccountInternalAsync(bool forceRefresh) this.isAccountRefreshInProgress = false; } } - } + } + internal async Task GetDatabaseAccountAsync(bool forceRefresh = false) { #nullable disable // Needed because AsyncCache does not have nullable enabled diff --git a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManager.cs b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManager.cs index a00045c402..e5e4740151 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManager.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManager.cs @@ -53,5 +53,31 @@ public abstract bool IsRequestEligibleForPartitionLevelCircuitBreaker( /// public abstract void SetBackgroundConnectionPeriodicRefreshTask( Func>, Task> backgroundConnectionInitTask); + + /// + /// Enables or disables per-partition automatic failover (PPAF) in a thread-safe manner. + /// This method sets the internal flag controlling whether automatic failover is allowed for partition key ranges. + /// + /// A boolean flag indicating the value to set. + public abstract void SetIsPPAFEnabled(bool isEnabled); + + /// + /// Enables or disables per-partition circuit breaker (PPCB) in a thread-safe manner. + /// This method sets the internal flag controlling whether circuit breaker logic is allowed for partition key ranges. + /// + /// A boolean flag indicating the value to set. + public abstract void SetIsPPCBEnabled(bool isEnabled); + + /// + /// Gets a value indicating whether per-partition automatic failover is currently enabled. + /// Returns true if automatic failover for partition key ranges is active, otherwise false. + /// + public abstract bool IsPartitionLevelAutomaticFailoverEnabled(); + + /// + /// Gets a value indicating whether per-partition circuit breaker is currently enabled. + /// Returns true if circuit breaker logic for partition key ranges is active, otherwise false. + /// + public abstract bool IsPartitionLevelCircuitBreakerEnabled(); } } diff --git a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs index d3e8a53292..52e4d9638a 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs @@ -48,21 +48,11 @@ internal sealed class GlobalPartitionEndpointManagerCore : GlobalPartitionEndpoi /// private readonly int backgroundConnectionInitTimeIntervalInSeconds = ConfigurationManager.GetStalePartitionUnavailabilityRefreshIntervalInSeconds(300); - /// - /// A readonly boolean flag used to determine if partition level failover is enabled. - /// - private readonly bool isPartitionLevelFailoverEnabled; - /// /// A readonly boolean flag used to determine if thinclient is enabled. /// private readonly bool isThinClientEnabled; - /// - /// A readonly boolean flag used to determine if partition level circuit breaker is enabled. - /// - private readonly bool isPartitionLevelCircuitBreakerEnabled; - /// /// A instance of containing the partition key range to failover info mapping. /// This mapping is primarily used for writes in a single master account. @@ -85,7 +75,17 @@ internal sealed class GlobalPartitionEndpointManagerCore : GlobalPartitionEndpoi /// /// A boolean flag indicating if the background connection initialization recursive task is active. /// - private bool isBackgroundConnectionInitActive = false; + private bool isBackgroundConnectionInitActive = false; + + /// + /// A boolean (represented as an int to allow for thread-safety) flag used to determine if partition level failover is enabled. + /// + private int isPartitionLevelAutomaticFailoverEnabled; + + /// + /// A boolean (represented as an int to allow for thread-safety) flag used to determine if partition level circuit breaker is enabled. + /// + private int isPartitionLevelCircuitBreakerEnabled; /// /// A callback func delegate used by the background connection refresh recursive task to establish rntbd connections to backend replicas. @@ -105,8 +105,8 @@ public GlobalPartitionEndpointManagerCore( bool isPartitionLevelCircuitBreakerEnabled = false, bool isThinClientEnabled = false) { - this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; - this.isPartitionLevelCircuitBreakerEnabled = isPartitionLevelCircuitBreakerEnabled; + this.isPartitionLevelAutomaticFailoverEnabled = isPartitionLevelFailoverEnabled ? 1 : 0; + this.isPartitionLevelCircuitBreakerEnabled = isPartitionLevelCircuitBreakerEnabled ? 1 : 0; this.isThinClientEnabled = isThinClientEnabled; this.globalEndpointManager = globalEndpointManager ?? throw new ArgumentNullException(nameof(globalEndpointManager)); this.InitializeAndStartCircuitBreakerFailbackBackgroundRefresh(); @@ -122,7 +122,7 @@ public override void SetBackgroundConnectionPeriodicRefreshTask( /// public override bool TryAddPartitionLevelLocationOverride( DocumentServiceRequest request) - { + { if (!this.IsRequestEligibleForPartitionFailover( request, shouldValidateFailedLocation: false, @@ -177,7 +177,7 @@ public override bool TryMarkEndpointUnavailableForPartitionKeyRange( { // For multi master write accounts, since all the regions are treated as write regions, the next locations to fail over // will be the preferred read regions that are configured in the application preferred regions in the CosmosClientOptions. - ReadOnlyCollection nextLocations = this.isThinClientEnabled && ThinClientStoreModel.IsOperationSupportedByThinClient(request) + ReadOnlyCollection nextLocations = this.isThinClientEnabled && GatewayStoreModel.IsOperationSupportedByThinClient(request) ? this.globalEndpointManager.ThinClientReadEndpoints : this.globalEndpointManager.ReadEndpoints; @@ -191,7 +191,7 @@ public override bool TryMarkEndpointUnavailableForPartitionKeyRange( else if (this.IsRequestEligibleForPerPartitionAutomaticFailover(request)) { // For any single master write accounts, the next locations to fail over will be the read regions configured at the account level. - ReadOnlyCollection nextLocations = this.isThinClientEnabled && ThinClientStoreModel.IsOperationSupportedByThinClient(request) + ReadOnlyCollection nextLocations = this.isThinClientEnabled && GatewayStoreModel.IsOperationSupportedByThinClient(request) ? this.globalEndpointManager.ThinClientReadEndpoints : this.globalEndpointManager.AccountReadEndpoints; @@ -261,7 +261,7 @@ public override bool IncrementRequestFailureCounterAndCheckIfPartitionCanFailove public override bool IsRequestEligibleForPerPartitionAutomaticFailover( DocumentServiceRequest request) { - return this.isPartitionLevelFailoverEnabled + return this.isPartitionLevelAutomaticFailoverEnabled == 1 && !request.IsReadOnlyRequest && !this.globalEndpointManager.CanSupportMultipleWriteLocations(request.ResourceType, request.OperationType); } @@ -277,11 +277,37 @@ public override bool IsRequestEligibleForPerPartitionAutomaticFailover( public override bool IsRequestEligibleForPartitionLevelCircuitBreaker( DocumentServiceRequest request) { - return this.isPartitionLevelCircuitBreakerEnabled + return this.isPartitionLevelCircuitBreakerEnabled == 1 && (request.IsReadOnlyRequest || (!request.IsReadOnlyRequest && this.globalEndpointManager.CanSupportMultipleWriteLocations(request.ResourceType, request.OperationType))); } + /// + public override void SetIsPPAFEnabled( + bool isPPAFEnabled) + { + Interlocked.Exchange(ref this.isPartitionLevelAutomaticFailoverEnabled, isPPAFEnabled ? 1 : 0); + } + + /// + public override void SetIsPPCBEnabled( + bool isPPCBEnabled) + { + Interlocked.Exchange(ref this.isPartitionLevelCircuitBreakerEnabled, isPPCBEnabled ? 1 : 0); + } + + /// + public override bool IsPartitionLevelAutomaticFailoverEnabled() + { + return this.isPartitionLevelAutomaticFailoverEnabled == 1; + } + + /// + public override bool IsPartitionLevelCircuitBreakerEnabled() + { + return this.isPartitionLevelCircuitBreakerEnabled == 1; + } + /// /// Disposes the class. /// Usage of the disposeCounter was used to make the operation atomic. @@ -313,6 +339,12 @@ private bool IsRequestEligibleForPartitionFailover( partitionKeyRange = default; failedLocation = default; + if (!this.IsPartitionLevelAutomaticFailoverEnabled() + && !this.IsPartitionLevelCircuitBreakerEnabled()) + { + return false; + } + if (request == null) { throw new ArgumentNullException(nameof(request)); @@ -529,7 +561,7 @@ private bool TryRouteRequestForPartitionLevelOverride( return false; } - string triggeredBy = this.isPartitionLevelFailoverEnabled ? "Automatic Failover" : "Circuit Breaker"; + string triggeredBy = this.isPartitionLevelAutomaticFailoverEnabled == 1 ? "Automatic Failover" : "Circuit Breaker"; DefaultTrace.TraceInformation("Attempting to route request for partition level override triggered by {0}, for operation type: {1}. URI: {2}, PartitionKeyRange: {3}", triggeredBy, request.OperationType, @@ -562,7 +594,7 @@ private bool TryAddOrUpdatePartitionFailoverInfoAndMoveToNextLocation( DocumentServiceRequest request, Lazy> partitionKeyRangeToLocationMapping) { - string triggeredBy = this.isPartitionLevelFailoverEnabled ? "Automatic Failover" : "Circuit Breaker"; + string triggeredBy = this.isPartitionLevelAutomaticFailoverEnabled == 1 ? "Automatic Failover" : "Circuit Breaker"; PartitionKeyRangeFailoverInfo partionFailover = partitionKeyRangeToLocationMapping.Value.GetOrAdd( partitionKeyRange, (_) => new PartitionKeyRangeFailoverInfo( diff --git a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerNoOp.cs b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerNoOp.cs index 67913b742f..231f5f9088 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerNoOp.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerNoOp.cs @@ -50,5 +50,25 @@ public override bool IncrementRequestFailureCounterAndCheckIfPartitionCanFailove { return false; } + + public override void SetIsPPAFEnabled(bool isEnabled) + { + return; + } + + public override void SetIsPPCBEnabled(bool isEnabled) + { + return; + } + + public override bool IsPartitionLevelAutomaticFailoverEnabled() + { + return false; + } + + public override bool IsPartitionLevelCircuitBreakerEnabled() + { + return false; + } } } diff --git a/Microsoft.Azure.Cosmos/src/Routing/PartitionKeyRangeCache.cs b/Microsoft.Azure.Cosmos/src/Routing/PartitionKeyRangeCache.cs index 17289aa975..97dac48318 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/PartitionKeyRangeCache.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/PartitionKeyRangeCache.cs @@ -31,12 +31,14 @@ internal class PartitionKeyRangeCache : IRoutingMapProvider, ICollectionRoutingM private readonly IStoreModel storeModel; private readonly CollectionCache collectionCache; private readonly IGlobalEndpointManager endpointManager; + private readonly bool useLengthAwareRangeComparer; public PartitionKeyRangeCache( ICosmosAuthorizationTokenProvider authorizationTokenProvider, IStoreModel storeModel, CollectionCache collectionCache, IGlobalEndpointManager endpointManager, + bool useLengthAwareRangeComparer, bool enableAsyncCacheExceptionNoSharing = true) { this.routingMapCache = new AsyncCacheNonBlocking( @@ -46,6 +48,7 @@ public PartitionKeyRangeCache( this.storeModel = storeModel; this.collectionCache = collectionCache; this.endpointManager = endpointManager; + this.useLengthAwareRangeComparer = useLengthAwareRangeComparer; } public virtual async Task> TryGetOverlappingRangesAsync( @@ -213,6 +216,16 @@ private async Task GetRoutingMapForCollectionAsync( lastStatusCode = response.StatusCode; changeFeedNextIfNoneMatch = response.Headers[HttpConstants.HttpHeaders.ETag]; + DefaultTrace.TraceInformation("PartitionKeyRangeCache GetRoutingMapForCollectionAsync collectionRid: {0}, StatusCode: {1}, SubstatusCode {2}, request Etag {3}, response ETag: {4}, RegionsContacted {5}", + collectionRid, + lastStatusCode, + response.GetSubStatusCodes(), + headers.GetHeaderValue(HttpConstants.HttpHeaders.IfNoneMatch), + changeFeedNextIfNoneMatch, + response.RequestStats?.RegionsContacted != null + ? string.Join(", ", response.RequestStats.RegionsContacted) + : string.Empty); + FeedResource feedResource = response.GetResource>(); if (feedResource != null) { @@ -232,11 +245,12 @@ private async Task GetRoutingMapForCollectionAsync( routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap( tuples.Where(tuple => !goneRanges.Contains(tuple.Item1.Id)), string.Empty, + false, changeFeedNextIfNoneMatch); } else { - routingMap = previousRoutingMap.TryCombine(tuples, changeFeedNextIfNoneMatch); + routingMap = previousRoutingMap.TryCombine(tuples, changeFeedNextIfNoneMatch, this.useLengthAwareRangeComparer); } if (routingMap == null) diff --git a/Microsoft.Azure.Cosmos/src/Routing/PartitionRoutingHelper.cs b/Microsoft.Azure.Cosmos/src/Routing/PartitionRoutingHelper.cs index 1c0f859eab..f6a346abb6 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/PartitionRoutingHelper.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/PartitionRoutingHelper.cs @@ -25,6 +25,7 @@ namespace Microsoft.Azure.Cosmos.Routing internal class PartitionRoutingHelper { + private static readonly bool IsLengthAwareComparisonEnabled = ConfigurationManager.IsLengthAwareRangeComparatorEnabled(); public static IReadOnlyList> GetProvidedPartitionKeyRanges( string querySpecJsonString, bool enableCrossPartitionQuery, @@ -231,9 +232,11 @@ await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync( return new ResolvedRangeInfo(lastPartitionKeyRange, suppliedTokens); } + (IComparer> minComparer, _) = this.GetComparers(IsLengthAwareComparisonEnabled); + Range minimumRange = PartitionRoutingHelper.Min( providedPartitionKeyRanges, - Range.MinComparer.Instance); + minComparer); return new ResolvedRangeInfo( await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync(collectionRid, minimumRange.Min, trace), @@ -347,6 +350,8 @@ public virtual async Task TryAddPartitionKeyRangeToContinuationTokenAsync( // We only need to get the next range if we have to if (string.IsNullOrEmpty(backendResponseHeaders[HttpConstants.HttpHeaders.Continuation])) { + (IComparer> minComparer, IComparer> maxComparer) = this.GetComparers(IsLengthAwareComparisonEnabled); + if (direction == RntdbEnumerationDirection.Reverse) { rangeToUse = PartitionRoutingHelper.MinBefore( @@ -354,14 +359,15 @@ public virtual async Task TryAddPartitionKeyRangeToContinuationTokenAsync( collectionRid, providedPartitionKeyRanges.Single(), trace)).ToList(), - currentRange); + currentRange, + minComparer); } else { Range nextProvidedRange = PartitionRoutingHelper.MinAfter( providedPartitionKeyRanges, currentRange.ToRange(), - Range.MaxComparer.Instance); + maxComparer); if (nextProvidedRange == null) { @@ -547,14 +553,14 @@ private static T MinAfter(IReadOnlyList values, T minValue, IComparer c return min; } - private static PartitionKeyRange MinBefore(IReadOnlyList values, PartitionKeyRange minValue) + private static PartitionKeyRange MinBefore(IReadOnlyList values, PartitionKeyRange minValue, + IComparer> comparer) { if (values.Count == 0) { throw new ArgumentException(nameof(values)); } - IComparer> comparer = Range.MinComparer.Instance; PartitionKeyRange min = null; foreach (PartitionKeyRange value in values) { @@ -566,6 +572,15 @@ private static PartitionKeyRange MinBefore(IReadOnlyList valu return min; } + + //LengthAwareComparer is the default Range comparer and flag is used to ovverride the default comparer to legacy Min/Max comparer. + private (IComparer> minComparer, IComparer> maxComparer) GetComparers(bool useLengthAwareComparison) + { + return ( + useLengthAwareComparison ? Range.LengthAwareMinComparer.Instance : Range.MinComparer.Instance, + useLengthAwareComparison ? Range.LengthAwareMaxComparer.Instance : Range.MaxComparer.Instance + ); + } public readonly struct ResolvedRangeInfo { diff --git a/Microsoft.Azure.Cosmos/src/Routing/RangeComparerProvider.cs b/Microsoft.Azure.Cosmos/src/Routing/RangeComparerProvider.cs new file mode 100644 index 0000000000..ac54e7fad5 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Routing/RangeComparerProvider.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Routing +{ + using System.Collections.Generic; + using Microsoft.Azure.Documents.Routing; + + internal class RangeComparerProvider + { + /// + /// Gets the minimum and maximum range comparers based on the current configuration. + /// + /// Tuple of (minComparer, maxComparer) + public static (IComparer> minComparer, IComparer> maxComparer) GetComparers(bool useLengthAwareComparison) + { + return ( + useLengthAwareComparison ? Range.LengthAwareMinComparer.Instance : Range.MinComparer.Instance, + useLengthAwareComparison ? Range.LengthAwareMaxComparer.Instance : Range.MaxComparer.Instance + ); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/SDKSupportedCapabilities.cs b/Microsoft.Azure.Cosmos/src/SDKSupportedCapabilities.cs deleted file mode 100644 index efcb4b3c31..0000000000 --- a/Microsoft.Azure.Cosmos/src/SDKSupportedCapabilities.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// ------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos -{ - using System; - - [Flags] - internal enum SDKSupportedCapabilities : ulong - { - None = 0, - PartitionMerge = 1 << 0, - } -} diff --git a/Microsoft.Azure.Cosmos/src/Serializer/CosmosElementSerializer.cs b/Microsoft.Azure.Cosmos/src/Serializer/CosmosElementSerializer.cs index 8981756db4..e4ed337909 100644 --- a/Microsoft.Azure.Cosmos/src/Serializer/CosmosElementSerializer.cs +++ b/Microsoft.Azure.Cosmos/src/Serializer/CosmosElementSerializer.cs @@ -8,7 +8,6 @@ namespace Microsoft.Azure.Cosmos.Serializer using System.IO; using System.Linq; using System.Runtime.InteropServices; - using System.Threading.Tasks; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Json; using Microsoft.Azure.Documents; diff --git a/Microsoft.Azure.Cosmos/src/Serializer/CosmosSerializationUtil.cs b/Microsoft.Azure.Cosmos/src/Serializer/CosmosSerializationUtil.cs index f316b1c18f..be835d2afb 100644 --- a/Microsoft.Azure.Cosmos/src/Serializer/CosmosSerializationUtil.cs +++ b/Microsoft.Azure.Cosmos/src/Serializer/CosmosSerializationUtil.cs @@ -6,14 +6,8 @@ namespace Microsoft.Azure.Cosmos { using System; using System.IO; - using System.Text; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.ChangeFeed; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Json; - using Microsoft.Azure.Cosmos.Json.Interop; - using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; - using Microsoft.Azure.Cosmos.Scripts; using Microsoft.Azure.Cosmos.Serializer; using Microsoft.Azure.Documents; using Newtonsoft.Json.Serialization; diff --git a/Microsoft.Azure.Cosmos/src/SqlObjects/SqlFunctionCallScalarExpression.cs b/Microsoft.Azure.Cosmos/src/SqlObjects/SqlFunctionCallScalarExpression.cs index 3877256961..01ef4e60e5 100644 --- a/Microsoft.Azure.Cosmos/src/SqlObjects/SqlFunctionCallScalarExpression.cs +++ b/Microsoft.Azure.Cosmos/src/SqlObjects/SqlFunctionCallScalarExpression.cs @@ -52,7 +52,8 @@ sealed class SqlFunctionCallScalarExpression : SqlScalarExpression { Names.Binary, Identifiers.Binary }, { Names.Float32, Identifiers.Float32 }, { Names.Float64, Identifiers.Float64 }, - { Names.Guid, Identifiers.Guid }, + { Names.Guid, Identifiers.Guid }, + { Names.Hash, Identifiers.Hash }, { Names.Int16, Identifiers.Int16 }, { Names.Int32, Identifiers.Int32 }, { Names.Int64, Identifiers.Int64 }, @@ -115,7 +116,9 @@ sealed class SqlFunctionCallScalarExpression : SqlScalarExpression { Names.Pi, Identifiers.Pi }, { Names.Power, Identifiers.Power }, { Names.Radians, Identifiers.Radians }, - { Names.Rand, Identifiers.Rand }, + { Names.Rand, Identifiers.Rand }, + { Names.RegexExtract, Identifiers.RegexExtract }, + { Names.RegexExtractAll, Identifiers.RegexExtractAll }, { Names.RegexMatch, Identifiers.RegexMatch }, { Names.Replace, Identifiers.Replace }, { Names.Replicate, Identifiers.Replicate }, @@ -322,6 +325,7 @@ public static class Names public const string GetCurrentDateTime = "GetCurrentDateTime"; public const string GetCurrentTicks = "GetCurrentTicks"; public const string GetCurrentTimestamp = "GetCurrentTimestamp"; + public const string Hash = "HASH"; public const string Iif = "IIF"; public const string IndexOf = "INDEX_OF"; public const string IntAdd = "IntAdd"; @@ -361,7 +365,9 @@ public static class Names public const string Pi = "PI"; public const string Power = "POWER"; public const string Radians = "RADIANS"; - public const string Rand = "RAND"; + public const string Rand = "RAND"; + public const string RegexExtract = "RegexExtract"; + public const string RegexExtractAll = "RegexExtractAll"; public const string RegexMatch = "RegexMatch"; public const string Replace = "REPLACE"; public const string Replicate = "REPLICATE"; @@ -497,6 +503,7 @@ public static class Identifiers public static readonly SqlIdentifier GetCurrentDateTime = SqlIdentifier.Create(Names.GetCurrentDateTime); public static readonly SqlIdentifier GetCurrentTicks = SqlIdentifier.Create(Names.GetCurrentTicks); public static readonly SqlIdentifier GetCurrentTimestamp = SqlIdentifier.Create(Names.GetCurrentTimestamp); + public static readonly SqlIdentifier Hash = SqlIdentifier.Create(Names.Hash); public static readonly SqlIdentifier Iif = SqlIdentifier.Create(Names.Iif); public static readonly SqlIdentifier IndexOf = SqlIdentifier.Create(Names.IndexOf); public static readonly SqlIdentifier IntAdd = SqlIdentifier.Create(Names.IntAdd); @@ -536,7 +543,9 @@ public static class Identifiers public static readonly SqlIdentifier Pi = SqlIdentifier.Create(Names.Pi); public static readonly SqlIdentifier Power = SqlIdentifier.Create(Names.Power); public static readonly SqlIdentifier Radians = SqlIdentifier.Create(Names.Radians); - public static readonly SqlIdentifier Rand = SqlIdentifier.Create(Names.Rand); + public static readonly SqlIdentifier Rand = SqlIdentifier.Create(Names.Rand); + public static readonly SqlIdentifier RegexExtract = SqlIdentifier.Create(Names.RegexExtract); + public static readonly SqlIdentifier RegexExtractAll = SqlIdentifier.Create(Names.RegexExtractAll); public static readonly SqlIdentifier RegexMatch = SqlIdentifier.Create(Names.RegexMatch); public static readonly SqlIdentifier Replace = SqlIdentifier.Create(Names.Replace); public static readonly SqlIdentifier Replicate = SqlIdentifier.Create(Names.Replicate); diff --git a/Microsoft.Azure.Cosmos/src/SqlObjects/SqlInScalarExpression.cs b/Microsoft.Azure.Cosmos/src/SqlObjects/SqlInScalarExpression.cs index 77002e7863..e98f6b2727 100644 --- a/Microsoft.Azure.Cosmos/src/SqlObjects/SqlInScalarExpression.cs +++ b/Microsoft.Azure.Cosmos/src/SqlObjects/SqlInScalarExpression.cs @@ -4,7 +4,6 @@ namespace Microsoft.Azure.Cosmos.SqlObjects { using System; - using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.Azure.Cosmos.SqlObjects.Visitors; diff --git a/Microsoft.Azure.Cosmos/src/SqlObjects/SqlLikeScalarExpression.cs b/Microsoft.Azure.Cosmos/src/SqlObjects/SqlLikeScalarExpression.cs index f439264be3..8a1485bd64 100644 --- a/Microsoft.Azure.Cosmos/src/SqlObjects/SqlLikeScalarExpression.cs +++ b/Microsoft.Azure.Cosmos/src/SqlObjects/SqlLikeScalarExpression.cs @@ -4,9 +4,6 @@ namespace Microsoft.Azure.Cosmos.SqlObjects { using System; - using System.Collections.Immutable; - using System.Linq; - using System.Text; using Microsoft.Azure.Cosmos.SqlObjects.Visitors; #if INTERNAL diff --git a/Microsoft.Azure.Cosmos/src/SqlObjects/SqlObjectCreateScalarExpression.cs b/Microsoft.Azure.Cosmos/src/SqlObjects/SqlObjectCreateScalarExpression.cs index 8876d64a02..091a480b69 100644 --- a/Microsoft.Azure.Cosmos/src/SqlObjects/SqlObjectCreateScalarExpression.cs +++ b/Microsoft.Azure.Cosmos/src/SqlObjects/SqlObjectCreateScalarExpression.cs @@ -4,7 +4,6 @@ namespace Microsoft.Azure.Cosmos.SqlObjects { using System; - using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.Azure.Cosmos.SqlObjects.Visitors; diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbNetworkMeter.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbNetworkMeter.cs index 43b2a15121..1687559b31 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbNetworkMeter.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbNetworkMeter.cs @@ -94,7 +94,6 @@ internal static void RecordTelemetry(Func getOperationName, { if (!IsEnabled || !CosmosDbMeterUtil.TryGetDiagnostics(attributes, ex, out CosmosTraceDiagnostics diagnostics)) { - DefaultTrace.TraceWarning("Network Meter is not enabled or Diagnostics is not available."); return; } diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs index b15a0a245a..656b280709 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs @@ -1,102 +1,121 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos -{ - using System; +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; using System.Collections.Concurrent; using System.Diagnostics; - using System.IO; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Documents; - using Newtonsoft.Json; - using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; - - /// - /// A TransportClient that sends requests to proxy endpoint. - /// And then processes the response back into DocumentServiceResponse objects. - /// - internal class ThinClientStoreClient : GatewayStoreClient - { - private readonly bool isPartitionLevelFailoverEnabled; - private readonly ObjectPool bufferProviderWrapperPool; + using System.IO; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.FaultInjection; + using Newtonsoft.Json; + using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; + + /// + /// A TransportClient that sends requests to proxy endpoint. + /// And then processes the response back into DocumentServiceResponse objects. + /// + internal class ThinClientStoreClient : GatewayStoreClient + { + private readonly GlobalPartitionEndpointManager globalPartitionEndpointManager; + private readonly ObjectPool bufferProviderWrapperPool; private readonly UserAgentContainer userAgentContainer; - - public ThinClientStoreClient( + private readonly IChaosInterceptor chaosInterceptor; + + public ThinClientStoreClient( CosmosHttpClient httpClient, - UserAgentContainer userAgentContainer, + UserAgentContainer userAgentContainer, ICommunicationEventSource eventSource, - bool isPartitionLevelFailoverEnabled = false, - JsonSerializerSettings serializerSettings = null) - : base(httpClient, - eventSource, - serializerSettings, - isPartitionLevelFailoverEnabled) - { - this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); - this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; - this.userAgentContainer = userAgentContainer; - } - - public override async Task InvokeAsync( - DocumentServiceRequest request, - ResourceType resourceType, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache, - CancellationToken cancellationToken) - { - using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( - request, - resourceType, - physicalAddress, - thinClientEndpoint, - globalDatabaseAccountName, - clientCollectionCache, - cancellationToken)) - { - HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); - return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); - } - } - - internal override async Task InvokeStoreAsync(Uri baseAddress, ResourceOperation resourceOperation, DocumentServiceRequest request) - { - Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? - HttpTransportClient.GetResourceFeedUri(resourceOperation.resourceType, baseAddress, request) : - HttpTransportClient.GetResourceEntryUri(resourceOperation.resourceType, baseAddress, request); - - using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( - request, - resourceOperation.resourceType, - physicalAddress, - default, - default, - default, - default)) - { - return await HttpTransportClient.ProcessHttpResponse(request.ResourceAddress, string.Empty, responseMessage, physicalAddress, request); - } - } - - private async ValueTask PrepareRequestForProxyAsync( - DocumentServiceRequest request, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache) - { - HttpRequestMessage requestMessage = base.PrepareRequestMessageAsync(request, physicalAddress).Result; - requestMessage.Version = new Version(2, 0); - - BufferProviderWrapper bufferProviderWrapper = this.bufferProviderWrapperPool.Get(); - try + GlobalPartitionEndpointManager globalPartitionEndpointManager, + JsonSerializerSettings serializerSettings = null, + IChaosInterceptor chaosInterceptor = null) + : base(httpClient, + eventSource, + globalPartitionEndpointManager, + serializerSettings) + { + this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); + this.globalPartitionEndpointManager = globalPartitionEndpointManager; + this.userAgentContainer = userAgentContainer + ?? throw new ArgumentNullException(nameof(userAgentContainer), + "UserAgentContainer cannot be null when initializing ThinClientStoreClient."); + this.chaosInterceptor = chaosInterceptor; + } + + public override async Task InvokeAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( + request, + resourceType, + physicalAddress, + thinClientEndpoint, + globalDatabaseAccountName, + clientCollectionCache, + cancellationToken)) + { + if (this.chaosInterceptor != null) + { + request.Headers.Set("FAULTINJECTION_IS_PROXY", "true"); + (bool hasFault, HttpResponseMessage fiResponseMessage) = await this.chaosInterceptor.OnHttpRequestCallAsync(request, cancellationToken); + if (hasFault) + { + DefaultTrace.TraceInformation("Chaos interceptor injected fault for request: {0}", request); + fiResponseMessage.RequestMessage = responseMessage.RequestMessage; + request.Headers.Remove("FAULTINJECTION_IS_PROXY"); + return await ThinClientStoreClient.ParseResponseAsync(fiResponseMessage, request.SerializerSettings ?? base.SerializerSettings, request); + } + } + + HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); + return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); + } + } + + internal override async Task InvokeStoreAsync(Uri baseAddress, ResourceOperation resourceOperation, DocumentServiceRequest request) + { + Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? + HttpTransportClient.GetResourceFeedUri(resourceOperation.resourceType, baseAddress, request) : + HttpTransportClient.GetResourceEntryUri(resourceOperation.resourceType, baseAddress, request); + + using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( + request, + resourceOperation.resourceType, + physicalAddress, + default, + default, + default, + default)) + { + return await HttpTransportClient.ProcessHttpResponse(request.ResourceAddress, string.Empty, responseMessage, physicalAddress, request); + } + } + + private async ValueTask PrepareRequestForProxyAsync( + DocumentServiceRequest request, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache) + { + HttpRequestMessage requestMessage = base.PrepareRequestMessageAsync(request, physicalAddress).Result; + requestMessage.Version = new Version(2, 0); + + BufferProviderWrapper bufferProviderWrapper = this.bufferProviderWrapperPool.Get(); + try { PartitionKeyRange partitionKeyRange = request.RequestContext?.ResolvedPartitionKeyRange; @@ -110,90 +129,90 @@ private async ValueTask PrepareRequestForProxyAsync( ThinClientConstants.ProxyEndEpk, partitionKeyRange?.MaxExclusive); } - - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.ProxyOperationType, - request.OperationType.ToOperationTypeString()); - - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.ProxyResourceType, - request.ResourceType.ToResourceTypeString()); - - Stream contentStream = await ThinClientTransportSerializer.SerializeProxyRequestAsync( - bufferProviderWrapper, - globalDatabaseAccountName, - clientCollectionCache, - requestMessage); - - if (!contentStream.CanSeek) - { - throw new InvalidOperationException( - $"The serializer returned a non-seekable stream ({contentStream.GetType().FullName})."); - } - - requestMessage.Content = new StreamContent(contentStream); - requestMessage.Content.Headers.ContentLength = contentStream.Length; + + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.ProxyOperationType, + request.OperationType.ToOperationTypeString()); + + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.ProxyResourceType, + request.ResourceType.ToResourceTypeString()); + + Stream contentStream = await ThinClientTransportSerializer.SerializeProxyRequestAsync( + bufferProviderWrapper, + globalDatabaseAccountName, + clientCollectionCache, + requestMessage); + + if (!contentStream.CanSeek) + { + throw new InvalidOperationException( + $"The serializer returned a non-seekable stream ({contentStream.GetType().FullName})."); + } + + requestMessage.Content = new StreamContent(contentStream); + requestMessage.Content.Headers.ContentLength = contentStream.Length; requestMessage.Headers.Clear(); - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.UserAgent, + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.UserAgent, this.userAgentContainer.UserAgent); - + Guid activityId = Trace.CorrelationManager.ActivityId; - Debug.Assert(activityId != Guid.Empty); - requestMessage.Headers.TryAddWithoutValidation( + Debug.Assert(activityId != Guid.Empty); + requestMessage.Headers.TryAddWithoutValidation( HttpConstants.HttpHeaders.ActivityId, activityId.ToString()); - requestMessage.RequestUri = thinClientEndpoint; - requestMessage.Method = HttpMethod.Post; - - return requestMessage; - } - finally - { - this.bufferProviderWrapperPool.Return(bufferProviderWrapper); - } - } - - private Task InvokeClientAsync( - DocumentServiceRequest request, - ResourceType resourceType, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache, - CancellationToken cancellationToken) - { - DefaultTrace.TraceInformation("In {0}, OperationType: {1}, ResourceType: {2}", nameof(ThinClientStoreClient), request.OperationType, request.ResourceType); - return base.httpClient.SendHttpAsync( - () => this.PrepareRequestForProxyAsync(request, physicalAddress, thinClientEndpoint, globalDatabaseAccountName, clientCollectionCache), - resourceType, - HttpTimeoutPolicy.GetTimeoutPolicy(request, isThinClientEnabled: true), - request.RequestContext.ClientRequestStatistics, + requestMessage.RequestUri = thinClientEndpoint; + requestMessage.Method = HttpMethod.Post; + + return requestMessage; + } + finally + { + this.bufferProviderWrapperPool.Return(bufferProviderWrapper); + } + } + + private Task InvokeClientAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + DefaultTrace.TraceInformation("In {0}, OperationType: {1}, ResourceType: {2}", nameof(ThinClientStoreClient), request.OperationType, request.ResourceType); + return base.httpClient.SendHttpAsync( + () => this.PrepareRequestForProxyAsync(request, physicalAddress, thinClientEndpoint, globalDatabaseAccountName, clientCollectionCache), + resourceType, + HttpTimeoutPolicy.GetTimeoutPolicy(request, isThinClientEnabled: true), + request.RequestContext.ClientRequestStatistics, cancellationToken, - request); - } - - internal class ObjectPool - { - private readonly ConcurrentBag Objects; - private readonly Func ObjectGenerator; - - public ObjectPool(Func objectGenerator) - { - this.ObjectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); - this.Objects = new ConcurrentBag(); - } - - public T Get() - { - return this.Objects.TryTake(out T item) ? item : this.ObjectGenerator(); - } - - public void Return(T item) - { - this.Objects.Add(item); - } - } - } + request); + } + + internal class ObjectPool + { + private readonly ConcurrentBag Objects; + private readonly Func ObjectGenerator; + + public ObjectPool(Func objectGenerator) + { + this.ObjectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); + this.Objects = new ConcurrentBag(); + } + + public T Get() + { + return this.Objects.TryTake(out T item) ? item : this.ObjectGenerator(); + } + + public void Return(T item) + { + this.Objects.Add(item); + } + } + } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs index 6b7a173980..a0be36e4d8 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs @@ -30,7 +30,7 @@ public ThinClientStoreModel( ConsistencyLevel defaultConsistencyLevel, DocumentClientEventSource eventSource, JsonSerializerSettings serializerSettings, - CosmosHttpClient httpClient, + CosmosHttpClient httpClient, UserAgentContainer userAgentContainer, bool isPartitionLevelFailoverEnabled = false) : base(endpointManager, @@ -43,10 +43,10 @@ public ThinClientStoreModel( isPartitionLevelFailoverEnabled) { this.thinClientStoreClient = new ThinClientStoreClient( - httpClient, + httpClient, userAgentContainer, - eventSource, - isPartitionLevelFailoverEnabled, + eventSource, + globalPartitionEndpointManager, serializerSettings); this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; @@ -134,7 +134,7 @@ await this.CaptureSessionTokenAndHandleSplitAsync( return response; } - public static bool IsOperationSupportedByThinClient( + public static new bool IsOperationSupportedByThinClient( DocumentServiceRequest request) { // Thin proxy supports the following operations for Document resources. diff --git a/Microsoft.Azure.Cosmos/src/ThinClientTransportSerializer.cs b/Microsoft.Azure.Cosmos/src/ThinClientTransportSerializer.cs index 36859cfd94..105e024922 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientTransportSerializer.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientTransportSerializer.cs @@ -113,7 +113,8 @@ public static async Task SerializeProxyRequestAsync( activityId, bufferProvider.Provider, accountName, - out _, + string.Empty, + out _, out _); int length = serializedRequest.RequestSize; diff --git a/Microsoft.Azure.Cosmos/src/Tracing/ITrace.cs b/Microsoft.Azure.Cosmos/src/Tracing/ITrace.cs index bd67890596..591345979b 100644 --- a/Microsoft.Azure.Cosmos/src/Tracing/ITrace.cs +++ b/Microsoft.Azure.Cosmos/src/Tracing/ITrace.cs @@ -6,7 +6,6 @@ namespace Microsoft.Azure.Cosmos.Tracing { using System; using System.Collections.Generic; - using System.Runtime.CompilerServices; /// /// Interface to represent a single node in a trace tree. @@ -68,6 +67,11 @@ interface ITrace : IDisposable /// IReadOnlyDictionary Data { get; } + /// + /// Gets a value indicating whether this trace is currently being walked. + /// + bool IsBeingWalked { get; } + /// /// Starts a Trace and adds it as a child to this instance. /// @@ -115,5 +119,12 @@ ITrace StartChild( /// Existing trace. void AddChild(ITrace trace); + /// + /// Tries to get a specific datum - it is safe to call this even before IsBeingWalked is set. + /// + /// The key to identify the datum. + /// The datum itself. + /// A flag indicating whether the datum with this key exists. + bool TryGetDatum(string key, out object datum); } } diff --git a/Microsoft.Azure.Cosmos/src/Tracing/NoOpTrace.cs b/Microsoft.Azure.Cosmos/src/Tracing/NoOpTrace.cs index b76e95fc5e..6077546f4a 100644 --- a/Microsoft.Azure.Cosmos/src/Tracing/NoOpTrace.cs +++ b/Microsoft.Azure.Cosmos/src/Tracing/NoOpTrace.cs @@ -40,6 +40,8 @@ private NoOpTrace() public IReadOnlyDictionary Data => NoOpData; + public bool IsBeingWalked => true; // this needs to return true to allow materialization of NoOpTrace + public void Dispose() { // NoOp @@ -86,5 +88,11 @@ public void UpdateRegionContacted(TraceDatum traceDatum) { // NoOp } + + bool ITrace.TryGetDatum(string key, out object datum) + { + datum = null; + return false; + } } } diff --git a/Microsoft.Azure.Cosmos/src/Tracing/Trace.cs b/Microsoft.Azure.Cosmos/src/Tracing/Trace.cs index cb644f1ff0..dfddd7131e 100644 --- a/Microsoft.Azure.Cosmos/src/Tracing/Trace.cs +++ b/Microsoft.Azure.Cosmos/src/Tracing/Trace.cs @@ -7,17 +7,16 @@ namespace Microsoft.Azure.Cosmos.Tracing using System; using System.Collections.Generic; using System.Diagnostics; - using System.Linq; - using System.Runtime.CompilerServices; - using Microsoft.Azure.Cosmos.Tracing.TraceData; using Microsoft.Azure.Documents; internal sealed class Trace : ITrace { private static readonly IReadOnlyDictionary EmptyDictionary = new Dictionary(); - private readonly List children; - private readonly Lazy> data; + private readonly object lockObject; + private volatile List children; + private volatile Dictionary data; private ValueStopwatch stopwatch; + private volatile bool isBeingWalked; private Trace( string name, @@ -27,6 +26,7 @@ private Trace( TraceSummary summary) { this.Name = name ?? throw new ArgumentNullException(nameof(name)); + this.lockObject = new object(); this.Id = Guid.NewGuid(); this.StartTime = DateTime.UtcNow; this.stopwatch = ValueStopwatch.StartNew(); @@ -34,7 +34,7 @@ private Trace( this.Component = component; this.Parent = parent; this.children = new List(); - this.data = new Lazy>(); + this.data = null; this.Summary = summary ?? throw new ArgumentNullException(nameof(summary)); } @@ -54,9 +54,37 @@ private Trace( public ITrace Parent { get; } - public IReadOnlyList Children => this.children; + // NOTE: no lock necessary here only because this.children is volatile + // and every reference to it is immutable when isBeingWalked == true + // and isBeingWalked is guaranteed to be set to true before this + // Property is called + public IReadOnlyList Children + { + get + { + // Assert that walking state is set + Debug.Assert(this.isBeingWalked, "SetWalkingStateRecursively should be set to true"); + + return this.children; + } + } + + // NOTE: no lock necessary here only because this.data is volatile + // and every reference to it is immutable when isBeingWalked == true + // and isBeingWalked is guaranteed to be set to true before this + // Property is called + public IReadOnlyDictionary Data + { + get + { + // Assert that walking state is set + Debug.Assert(this.isBeingWalked, "SetWalkingStateRecursively should be set to true"); + + return this.data ?? Trace.EmptyDictionary; + } + } - public IReadOnlyDictionary Data => this.data.IsValueCreated ? this.data.Value : Trace.EmptyDictionary; + public bool IsBeingWalked => this.isBeingWalked; public void Dispose() { @@ -90,14 +118,30 @@ public ITrace StartChild( summary: this.Summary); this.AddChild(child); + return child; } public void AddChild(ITrace child) { - lock (this.children) + lock (this.lockObject) { - this.children.Add(child); + if (!this.isBeingWalked) + { + this.children.Add(child); + + return; + } + + if (child is Trace traceChild) + { + traceChild.SetWalkingStateRecursively(); + } + + List writableSnapshot = new List(this.children.Count + 1); + writableSnapshot.AddRange(this.children); + writableSnapshot.Add(child); + this.children = writableSnapshot; } } @@ -124,18 +168,89 @@ public static Trace GetRootTrace( public void AddDatum(string key, TraceDatum traceDatum) { - this.data.Value.Add(key, traceDatum); this.Summary.UpdateRegionContacted(traceDatum); + this.AddDatum(key, traceDatum as Object); } public void AddDatum(string key, object value) { - this.data.Value.Add(key, value); + lock (this.lockObject) + { + this.data ??= new Dictionary(); + + if (!this.isBeingWalked) + { + // If materialization has not started yet no cloning is needed + this.data.Add(key, value); + return; + } + + this.data = new Dictionary(this.data) + { + { key, value } + }; + } } public void AddOrUpdateDatum(string key, object value) { - this.data.Value[key] = value; + lock (this.lockObject) + { + this.data ??= new Dictionary(); + + if (!this.isBeingWalked) + { + // If materialization has not started yet no cloning is needed + this.data[key] = value; + return; // Ignore modifications while being walked + } + + this.data = new Dictionary(this.data) + { + [key] = value + }; + } + } + + internal void SetWalkingStateRecursively() + { + if (this.isBeingWalked) + { + return; // Already set, return early + } + + lock (this.lockObject) + { + if (this.isBeingWalked) + { + return; // Already set, return early + } + + foreach (ITrace child in this.children) + { + if (child is Trace concreteChild) + { + concreteChild.SetWalkingStateRecursively(); + } + } + + // Set the walking state for this trace after processing children + this.isBeingWalked = true; + } + } + + bool ITrace.TryGetDatum(string key, out object datum) + { + lock (this.lockObject) + { + if (this.data == null) + { + datum = null; + return false; + } + + return this.data.TryGetValue(key, out datum); + } } } } diff --git a/Microsoft.Azure.Cosmos/src/Tracing/TraceData/SummaryDiagnostics.cs b/Microsoft.Azure.Cosmos/src/Tracing/TraceData/SummaryDiagnostics.cs index 64f3414529..f1f708c1b7 100644 --- a/Microsoft.Azure.Cosmos/src/Tracing/TraceData/SummaryDiagnostics.cs +++ b/Microsoft.Azure.Cosmos/src/Tracing/TraceData/SummaryDiagnostics.cs @@ -6,6 +6,7 @@ namespace Microsoft.Azure.Cosmos.Tracing.TraceData { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Globalization; using System.Linq; using Microsoft.Azure.Cosmos.Json; @@ -22,7 +23,11 @@ public SummaryDiagnostics(ITrace trace) = new Lazy>(() => new Dictionary<(int, int), int>()); this.AllRegionsContacted = new Lazy>(() => new HashSet()); - + + if (trace is Tracing.Trace rootConcreteTrace) + { + rootConcreteTrace.SetWalkingStateRecursively(); + } this.CollectSummaryFromTraceTree(trace); } @@ -38,6 +43,9 @@ public SummaryDiagnostics(ITrace trace) private void CollectSummaryFromTraceTree(ITrace currentTrace) { + // Assert that walking state is set + Debug.Assert(currentTrace.IsBeingWalked, "SetWalkingStateRecursively should be set to true"); + foreach (object datums in currentTrace.Data.Values) { if (datums is ClientSideRequestStatisticsTraceDatum clientSideRequestStatisticsTraceDatum) diff --git a/Microsoft.Azure.Cosmos/src/Tracing/TraceSummary.cs b/Microsoft.Azure.Cosmos/src/Tracing/TraceSummary.cs index 69f1ae9e37..1923dc8524 100644 --- a/Microsoft.Azure.Cosmos/src/Tracing/TraceSummary.cs +++ b/Microsoft.Azure.Cosmos/src/Tracing/TraceSummary.cs @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.Tracing using System; using System.Collections.Generic; using System.Linq; - using System.Text; using System.Threading; using Microsoft.Azure.Cosmos.Tracing.TraceData; diff --git a/Microsoft.Azure.Cosmos/src/Tracing/TraceWriter.TraceJsonWriter.cs b/Microsoft.Azure.Cosmos/src/Tracing/TraceWriter.TraceJsonWriter.cs index f3880a517c..7ca9632b65 100644 --- a/Microsoft.Azure.Cosmos/src/Tracing/TraceWriter.TraceJsonWriter.cs +++ b/Microsoft.Azure.Cosmos/src/Tracing/TraceWriter.TraceJsonWriter.cs @@ -8,7 +8,6 @@ namespace Microsoft.Azure.Cosmos.Tracing using System.Collections.Generic; using System.Globalization; using System.Linq; - using System.Net.Http; using System.Text; using Microsoft.Azure.Cosmos.Json; using Microsoft.Azure.Cosmos.Tracing.TraceData; @@ -130,9 +129,14 @@ private static void WriteTraceDatum(IJsonWriter writer, object value) { writer.WriteStringValue(stringValue); } - else - { - writer.WriteStringValue(value.ToString()); + else if (value is CosmosOperationCanceledException cosmosTimeoutException) + { + writer.WriteStringValue( + cosmosTimeoutException.EnsureToStringMessage(skipDiagnostics: true)); + } + else + { + writer.WriteStringValue(value.ToString()); } } @@ -404,7 +408,10 @@ public void Visit(StoreResult storeResult) this.WriteStringValueOrNull(storeResult.PartitionKeyRangeId); this.jsonWriter.WriteFieldName(nameof(storeResult.GlobalCommittedLSN)); - this.jsonWriter.WriteNumberValue(storeResult.GlobalCommittedLSN); + this.jsonWriter.WriteNumberValue(storeResult.GlobalCommittedLSN); + + this.jsonWriter.WriteFieldName(nameof(storeResult.GlobalNRegionCommittedGLSN)); + this.jsonWriter.WriteNumberValue(storeResult.GlobalNRegionCommittedGLSN); this.jsonWriter.WriteFieldName(nameof(storeResult.ItemLSN)); this.jsonWriter.WriteNumberValue(storeResult.ItemLSN); @@ -448,7 +455,7 @@ public void Visit(StoreResult storeResult) this.jsonWriter.WriteFieldName("TransportException"); TransportException transportException = storeResult.Exception?.InnerException as TransportException; - this.WriteStringValueOrNull(transportException?.Message); + this.WriteStringValueOrNull(transportException?.Message); this.jsonWriter.WriteObjectEnd(); } diff --git a/Microsoft.Azure.Cosmos/src/UserAgentContainer.cs b/Microsoft.Azure.Cosmos/src/UserAgentContainer.cs index ae9e3d4457..14a8f9fcb5 100644 --- a/Microsoft.Azure.Cosmos/src/UserAgentContainer.cs +++ b/Microsoft.Azure.Cosmos/src/UserAgentContainer.cs @@ -12,8 +12,11 @@ internal class UserAgentContainer : Documents.UserAgentContainer { private const int MaxOperatingSystemString = 30; private const int MaxClientId = 10; + private const string PipeDelimiter = "|"; + private readonly string cosmosBaseUserAgent; private readonly string clientId; + private static readonly Regex regex = new Regex(@"F\d+\|", RegexOptions.Compiled); public UserAgentContainer( int clientId, @@ -34,10 +37,40 @@ public void AppendFeatures( { if (!string.IsNullOrEmpty(features)) { + // Here we have 3 scenarios: + // 1. Suffix is empty, we just set it to the features. + // 2. Suffix is not empty, we append the features to the existing suffix. + // 3. Suffix already contains features, we the new features in the existing suffix. this.Suffix = string.IsNullOrEmpty(this.Suffix) ? features - : $"{features}|{this.Suffix}"; + : this.HasFeatureFlag() + ? $"{features}{this.Suffix.Substring(this.Suffix.IndexOf(UserAgentContainer.PipeDelimiter))}" + : $"{features}{UserAgentContainer.PipeDelimiter}{this.Suffix}"; + } + else + { + // Here we have 3 scenarios: + // 1. Suffix is empty, we just set it to empty. + // 2. Suffix is not empty, we remove the features from the existing suffix. + // 3. Suffix already contains features, we remove the features from the existing suffix. + this.Suffix = string.IsNullOrEmpty(this.Suffix) + ? string.Empty + : this.HasFeatureFlag() + //if the suffix contains a feature flag we can assume that the first pipe delimiter marks the end of it + ? this.Suffix.Substring(this.Suffix.IndexOf(UserAgentContainer.PipeDelimiter) + 1) + : this.Suffix; + } + } + + private bool HasFeatureFlag() + { + if (string.IsNullOrEmpty(this.Suffix)) + { + return false; } + + // Matches 'F' followed by one or more digits, then a pipe '|' + return regex.IsMatch(this.Suffix); } internal override string BaseUserAgent => this.cosmosBaseUserAgent ?? string.Empty; diff --git a/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs b/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs index 0923257e20..b5b4d8eec2 100644 --- a/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs +++ b/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs @@ -117,6 +117,14 @@ internal static class ConfigurationManager /// internal static readonly string BypassQueryParsing = "AZURE_COSMOS_BYPASS_QUERY_PARSING"; + /// + /// A read-only string containing the environment variable name for disabling length aware range comparator. + /// Length aware range comparators were intorduced in Range class to handle EPK range comparisons correctly in the case of a container's physical partition set consisting of fully and partially specified EPK values. + /// By default length aware range comparator is enabled. Refer to Range.cs in Msdata project for more details. Range.LengthAwareMinComparer/LengthAwareMaxComparer. + /// Setting the value to false will disable length aware range comparator and switch to using the regular Range.MinComparer/MaxComparer. + /// + internal static readonly string UseLengthAwareRangeComparator = "AZURE_COSMOS_USE_LENGTH_AWARE_RANGE_COMPARATOR"; + public static T GetEnvironmentVariable(string variable, T defaultValue) { string value = Environment.GetEnvironmentVariable(variable); @@ -376,5 +384,22 @@ public static bool ForceBypassQueryParsing() variable: ConfigurationManager.BypassQueryParsing, defaultValue: false); } + + /// + /// Gets the boolean value indicating if length-aware range comparator is enabled. + /// Default: true for preview , false for GA. + /// + /// A boolean flag indicating if length-aware range comparator is enabled. + public static bool IsLengthAwareRangeComparatorEnabled() + { + bool defaultValue = false; +#if PREVIEW && !INTERNAL + defaultValue = true; +#endif + return ConfigurationManager + .GetEnvironmentVariable( + variable: ConfigurationManager.UseLengthAwareRangeComparator, + defaultValue: defaultValue); + } } } diff --git a/Microsoft.Azure.Cosmos/src/direct/AddressEnumerator.cs b/Microsoft.Azure.Cosmos/src/direct/AddressEnumerator.cs index f0f00372a6..8d78030da4 100644 --- a/Microsoft.Azure.Cosmos/src/direct/AddressEnumerator.cs +++ b/Microsoft.Azure.Cosmos/src/direct/AddressEnumerator.cs @@ -325,22 +325,15 @@ private static IEnumerable MoveFailedReplicasToTheEnd( } } } -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row - -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -/// + /// /// Gets the effective health status of the transport address uri. /// /// An instance of the containing the replica address. /// A set containing the failed endpoints. /// An instance of indicating the effective health status of the address. private static TransportAddressHealthState.HealthStatus GetEffectiveStatus( -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row -#pragma warning disable SA1114 // Parameter list should follow declaration TransportAddressUri addressUri, -#pragma warning restore SA1114 // Parameter list should follow declaration -#pragma warning restore SA1114 // Parameter list should follow declaration ConcurrentDictionary failedEndpoints) { if (failedEndpoints != null && failedEndpoints.ContainsKey(addressUri)) diff --git a/Microsoft.Azure.Cosmos/src/direct/AzureRbac.cs b/Microsoft.Azure.Cosmos/src/direct/AzureRbac.cs new file mode 100644 index 0000000000..8914db6f5a --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/direct/AzureRbac.cs @@ -0,0 +1,104 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Documents +{ + using System; + using System.Linq; + + /// + /// Resource representing an Azure RBAC resource for a Cosmos DB account. + /// It can hold data for an Azure RBAC role assignment or role definition. + /// + internal sealed class AzureRbac : Resource + { + + public AzureRbac() + { + } + + public AzureRbac( + string id, + byte[] data) + { + this.Id = id; + this.Data = data; + } + + /// + /// Data for the Azure RBAC resource. + /// + public byte[] Data + { + get + { + byte[] dataValue = base.GetValue(AzureRbac.SerializationConstants.Data); + + if (dataValue == null || dataValue.Length == 0) + { + throw new FormatException( + string.Format( + AzureRbac.SerializationErrors.MissingRequiredProperty, + AzureRbac.SerializationConstants.Data)); + } + + return dataValue; + } + internal set + { + // Store as Base64 string to ensure consistency between in-memory and deserialized representations. + // When a byte[] is stored via JToken.FromObject(), it creates a JTokenType.Bytes token. + // However, after JSON serialization and deserialization, it becomes a JTokenType.String (Base64). + // This mismatch causes checkpoint verification failures in OperationStateManager. + // By explicitly storing as Base64 string, both representations will match. + base.SetValue(AzureRbac.SerializationConstants.Data, value != null ? Convert.ToBase64String(value) : null); + } + } + + internal override void Validate() + { + base.Validate(); + + if (string.IsNullOrWhiteSpace(this.Id)) + { + throw new FormatException( + string.Format( + AzureRbac.SerializationErrors.MissingRequiredProperty, + Constants.Properties.Id)); + } + + _ = this.Data; + } + + public static bool AreEquivalent(AzureRbac a, AzureRbac b) + { + if (a == null || b == null) + { + return a == null && b == null; + } + + if (!string.Equals(a.Id, b.Id, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (a.Data == null || b.Data == null) + { + return a.Data == null && b.Data == null; + } + + return a.Data.SequenceEqual(b.Data); + } + + private static class SerializationConstants + { + public const string Data = "data"; + } + + internal static class SerializationErrors + { + public const string MissingRequiredProperty = "Required property [{0}] is not present or is empty."; + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/direct/BarrierRequestHelper.cs b/Microsoft.Azure.Cosmos/src/direct/BarrierRequestHelper.cs index 898177c41e..0fa0b14803 100644 --- a/Microsoft.Azure.Cosmos/src/direct/BarrierRequestHelper.cs +++ b/Microsoft.Azure.Cosmos/src/direct/BarrierRequestHelper.cs @@ -9,6 +9,7 @@ namespace Microsoft.Azure.Documents using System.Globalization; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Documents.Collections; internal static class BarrierRequestHelper { @@ -62,9 +63,7 @@ public static async Task CreateAsync( headers: null, authorizationTokenType: originalRequestTokenType); } -#pragma warning disable SA1108 else if (request.IsNameBased) // Name based server request -#pragma warning restore SA1108 { // get the collection full name // dbs/{id}/colls/{collid}/ @@ -76,9 +75,7 @@ public static async Task CreateAsync( originalRequestTokenType, null); } -#pragma warning disable SA1108 else // RID based Server request -#pragma warning restore SA1108 { barrierLsnRequest = DocumentServiceRequest.Create( OperationType.Head, @@ -86,7 +83,17 @@ public static async Task CreateAsync( ResourceType.Collection, null, originalRequestTokenType); } + if (ShouldAllowBarrierRequestWithRwStatusRevoked(request)) + { + barrierLsnRequest.Headers[WFConstants.BackendHeaders.AllowBarrierRequestWhenRWStatusRevoked] = bool.TrueString; + } + barrierLsnRequest.Headers[HttpConstants.HttpHeaders.XDate] = Rfc1123DateTimeCache.UtcNow(); + barrierLsnRequest.UseStatusCodeFor429 = request.UseStatusCodeFor429; + barrierLsnRequest.UseStatusCodeForFailures = request.UseStatusCodeForFailures; + barrierLsnRequest.UseStatusCodeFor403 = request.UseStatusCodeFor403; + barrierLsnRequest.UseStatusCodeFor4041002 = request.UseStatusCodeFor4041002; + barrierLsnRequest.UseStatusCodeForBadRequest = request.UseStatusCodeForBadRequest; if (targetLsn.HasValue && targetLsn.Value > 0) { @@ -192,6 +199,13 @@ internal static bool IsOldBarrierRequestHandlingEnabled } } + internal static bool ShouldAllowBarrierRequestWithRwStatusRevoked(DocumentServiceRequest request) + { + return request?.Headers != null && + bool.TryParse(request.Headers[WFConstants.BackendHeaders.AllowBarrierRequestWhenRWStatusRevoked], + out bool isAllowed) && isAllowed; + } + internal static bool IsCollectionHeadBarrierRequest(ResourceType resourceType, OperationType operationType) { switch(resourceType) @@ -226,15 +240,12 @@ internal static bool IsCollectionHeadBarrierRequest(ResourceType resourceType, O return false; } } -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row - -#pragma warning disable CS1570 // XML comment has badly formed XML -/// + /// /// Used to determine the appropriate back-off time between barrier requests based /// on the responses to previous barrier requests. The substatus code of HEAD requests /// indicate the gap - like how far the targeted LSN/GCLSN was missed. - /// As a very naive rule-of-thumb the assumpiton is that even for small documents < 1 KB + /// As a very naive rule-of-thumb the assumpiton is that even for small documents < 1 KB /// only about 2000 write trasnactions can possibly be committed on a single phsyical /// partition (10,000 RU / 5 RU at least per write operation). The allowed /// throughput per physical partition could grow and the min. RU per write operations @@ -256,8 +267,6 @@ internal static bool IsCollectionHeadBarrierRequest(ResourceType resourceType, O /// A flag indicating whether a delay before the next barrier request should be injected. /// internal static bool ShouldDelayBetweenHeadRequests( -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row -#pragma warning restore CS1570 // XML comment has badly formed XML TimeSpan previousHeadRequestLatency, IList> responses, TimeSpan minDelay, @@ -297,6 +306,32 @@ internal static bool ShouldDelayBetweenHeadRequests( return minDelay > TimeSpan.Zero; } + /// + /// Checks if any response in the list is a 410/1022 Gone error. + /// + internal static bool IsGoneLeaseNotFound(IList> responses) + { + if (responses == null) return false; + foreach (ReferenceCountedDisposable storeResult in responses) + { + if (IsGoneLeaseNotFound(storeResult.Target)) + { + return true; + } + } + return false; + } + + /// + /// Checks if the given StoreResult is a 410/1022 Gone error. + /// + internal static bool IsGoneLeaseNotFound(StoreResult r) + { + return r != null && + r.StatusCode == StatusCodes.Gone && + r.SubStatusCode == SubStatusCodes.LeaseNotFound; + } + private static TimeSpan GetDelayBetweenHeadRequests(int minLSNGap) { if (minLSNGap == Int32.MaxValue) diff --git a/Microsoft.Azure.Cosmos/src/direct/BarrierType.cs b/Microsoft.Azure.Cosmos/src/direct/BarrierType.cs new file mode 100644 index 0000000000..c3c38f17ed --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/direct/BarrierType.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Documents +{ + /// + /// Defines the barrier request types. + /// + internal enum BarrierType + { + /// + /// Represents No barrier needed. + /// + None = 0, + + /// + /// Represents barrier for global strong consistency writes. + /// + GlobalStrongWrite = 1, + + /// + /// Represents barrier for N-region synchronous commit writes. + /// + NRegionSynchronousCommit = 2 + } +} diff --git a/Microsoft.Azure.Cosmos/src/direct/Channel.cs b/Microsoft.Azure.Cosmos/src/direct/Channel.cs index 290f96eded..705a408e79 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Channel.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Channel.cs @@ -54,6 +54,8 @@ public Channel( channelProperties.EnableChannelMultiplexing, channelProperties.MemoryStreamPool, channelProperties.RemoteCertificateValidationCallback, + channelProperties.ClientCertificateFunction, + channelProperties.ClientCertificateFailureHandler, channelProperties.DnsResolutionFunction, chaosInterceptor); this.timerPool = channelProperties.RequestTimerPool; @@ -556,14 +558,22 @@ private static void HandleTaskTimeout(Task runawayTask, Guid activityId, Guid co Task ignored = runawayTask.ContinueWith(task => { Trace.CorrelationManager.ActivityId = activityId; - Debug.Assert(task.IsFaulted); - Debug.Assert(task.Exception != null); - Exception e = task.Exception.InnerException; - DefaultTrace.TraceInformation( - "[RNTBD Channel {0}] Timed out task completed. Activity ID = {1}. HRESULT = {2:X}. Exception: {3}", - connectionCorrelationId, activityId, e?.HResult, e?.Message); + + if (task.IsFaulted && task.Exception != null) + { + Exception e = task.Exception.InnerException; + DefaultTrace.TraceInformation( + "[RNTBD Channel {0}] Timed out task completed with fault. Activity ID = {1}. HRESULT = {2:X}. Exception: {3}", + connectionCorrelationId, activityId, e?.HResult, e?.Message); + } + else if (task.IsCanceled) + { + DefaultTrace.TraceInformation( + "[RNTBD Channel {0}] Timed out task completed with cancellation. Activity ID = {1}.", + connectionCorrelationId, activityId); + } }, - TaskContinuationOptions.OnlyOnFaulted); + TaskContinuationOptions.NotOnRanToCompletion); // Captures both faulted and canceled states } private enum State diff --git a/Microsoft.Azure.Cosmos/src/direct/ChannelDictionary.cs b/Microsoft.Azure.Cosmos/src/direct/ChannelDictionary.cs index 1ed3f210ef..aebd505a96 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ChannelDictionary.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ChannelDictionary.cs @@ -15,9 +15,7 @@ internal sealed class ChannelDictionary : IChannelDictionary, IDisposable private readonly ChannelProperties channelProperties; private bool disposed = false; -#pragma warning disable IDE0044 // Add readonly modifier private ConcurrentDictionary channels = -#pragma warning restore IDE0044 // Add readonly modifier new ConcurrentDictionary(); private readonly IChaosInterceptor chaosInterceptor; diff --git a/Microsoft.Azure.Cosmos/src/direct/ChannelProperties.cs b/Microsoft.Azure.Cosmos/src/direct/ChannelProperties.cs index b074761440..bfbe0d39ff 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ChannelProperties.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ChannelProperties.cs @@ -8,6 +8,7 @@ namespace Microsoft.Azure.Documents.Rntbd using System.Net; using System.Net.Security; using System.Threading.Tasks; + using System.Security.Cryptography.X509Certificates; internal sealed class ChannelProperties { @@ -22,6 +23,8 @@ public ChannelProperties(UserAgentContainer userAgent, RntbdConstants.CallerId callerId, bool enableChannelMultiplexing, MemoryStreamPool memoryStreamPool, RemoteCertificateValidationCallback remoteCertificateValidationCallback, + Func clientCertificateFunction, + Action clientCertificateFailureHandler, Func> dnsResolutionFunction) { Debug.Assert(userAgent != null); @@ -56,6 +59,8 @@ public ChannelProperties(UserAgentContainer userAgent, this.MaxConcurrentOpeningConnectionCount = maxConcurrentOpeningConnectionCount; this.MemoryStreamPool = memoryStreamPool; this.RemoteCertificateValidationCallback = remoteCertificateValidationCallback; + this.ClientCertificateFunction = clientCertificateFunction; + this.ClientCertificateFailureHandler = clientCertificateFailureHandler; this.DnsResolutionFunction = dnsResolutionFunction; } @@ -107,6 +112,10 @@ public ChannelProperties(UserAgentContainer userAgent, public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; private set; } + public Func ClientCertificateFunction { get; private set; } + + public Action ClientCertificateFailureHandler { get; private set; } + public Func> DnsResolutionFunction { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/direct/ClientSideRequestStatistics.cs b/Microsoft.Azure.Cosmos/src/direct/ClientSideRequestStatistics.cs index 38f624196a..be0346773e 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ClientSideRequestStatistics.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ClientSideRequestStatistics.cs @@ -21,7 +21,6 @@ internal sealed class ClientSideRequestStatistics : IClientSideRequestStatistics private const int MaxSupplementalRequestsForToString = 10; private static bool enableCpuMonitorFlag; -#pragma warning disable IDE0044 // Add readonly modifier private DateTime requestStartTime; private DateTime? requestEndTime; @@ -32,7 +31,6 @@ internal sealed class ClientSideRequestStatistics : IClientSideRequestStatistics private List supplementalResponseStatisticsList; private Dictionary addressResolutionStatistics; private Lazy> httpResponseStatisticsList; -#pragma warning restore IDE0044 // Add readonly modifier private SystemUsageHistory systemUsageHistory; static ClientSideRequestStatistics() @@ -535,8 +533,5 @@ public void AppendToBuilder(StringBuilder stringBuilder) } } } -#pragma warning disable SA1518 // Use line endings correctly at end of file } - -#pragma warning restore SA1518 // Use line endings correctly at end of file \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/direct/Connection.cs b/Microsoft.Azure.Cosmos/src/direct/Connection.cs index 7904dc4647..fa6af51e1a 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Connection.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Connection.cs @@ -15,6 +15,7 @@ namespace Microsoft.Azure.Documents.Rntbd using System.Security.Authentication; using System.Threading; using System.Threading.Tasks; + using System.Security.Cryptography.X509Certificates; using Microsoft.Azure.Cosmos.Core.Trace; #if COSMOSCLIENT @@ -71,6 +72,8 @@ internal sealed class Connection : IConnection, IDisposable // Used only for integration tests private readonly RemoteCertificateValidationCallback remoteCertificateValidationCallback; + private readonly Func clientCertificateFunction; + private bool disposed = false; private TcpClient tcpClient; @@ -121,6 +124,7 @@ public Connection( TimeSpan idleTimeout, MemoryStreamPool memoryStreamPool, RemoteCertificateValidationCallback remoteCertificateValidationCallback, + Func clientCertificateFunction, Func> dnsResolutionFunction) { Debug.Assert(serverUri.PathAndQuery.Equals("/", StringComparison.Ordinal), serverUri.AbsoluteUri, @@ -149,6 +153,7 @@ public Connection( this.memoryStreamPool = memoryStreamPool; this.remoteCertificateValidationCallback = remoteCertificateValidationCallback; + this.clientCertificateFunction = clientCertificateFunction; this.healthChecker = new ( sendDelayLimit: sendHangDetectionTime, @@ -641,8 +646,17 @@ private async Task NegotiateSslAsync(ChannelOpenArguments args) TransportErrorCode.SslNegotiationTimeout); this.UpdateLastSendAttemptTime(); - await sslStream.AuthenticateAsClientAsync(host, clientCertificates: null, - enabledSslProtocols: Connection.TlsProtocols, checkCertificateRevocation: false); + X509CertificateCollection clientCertificates = null; + if (this.clientCertificateFunction != null) + { + X509Certificate2 clientCertificate = this.clientCertificateFunction(host); + if (clientCertificate != null) + { + clientCertificates = new X509CertificateCollection() { clientCertificate }; + } + } + await sslStream.AuthenticateAsClientAsync(host, clientCertificates, + enabledSslProtocols: Connection.TlsProtocols, checkCertificateRevocation: false); this.UpdateLastSendTime(); this.UpdateLastReceiveTime(); diff --git a/Microsoft.Azure.Cosmos/src/direct/ConnectionStateMuxListener.cs b/Microsoft.Azure.Cosmos/src/direct/ConnectionStateMuxListener.cs index 557d95ccbc..dc859e15c5 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ConnectionStateMuxListener.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ConnectionStateMuxListener.cs @@ -12,11 +12,7 @@ namespace Microsoft.Azure.Documents using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Documents.Rntbd; -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row - -#pragma warning disable CS1570 // XML comment has badly formed XML -#pragma warning disable CS1584 // XML comment has syntactically incorrect cref attribute /// /// ConnectionStateListener listens to the connection reset event notification fired by the transport client /// and refreshes the Document client's address cache @@ -29,30 +25,27 @@ namespace Microsoft.Azure.Documents /// /// control the number of concurrent notifications or calles to the registered handlers /// - Default: Environment.ProcessorCount - /// - Can be set through + /// - Can be set through IStoreClientFactory.GetConnectionStateListener().SetConnectionEventConcurrency(int notificationConcurrency) /// - ZERO: no notifications will be sent /// /// can be used in conjunction with this listener /// to control at the account level with the above combinations (AND clause) /// /// A safe phased roll-out for service might be: - /// - =False - /// - (Restart) =True & =0 (Subscription only non notifications) - /// - =True & > 0 + /// - enableTcpConnectionEndpointRediscovery=False + /// - (Restart) enableTcpConnectionEndpointRediscovery=True and notificationConcurrency=0 (Subscription only non notifications) + /// - enableTcpConnectionEndpointRediscovery=True and notificationConcurrency > 0 /// - When upgrade resiliency will be fully ON /// /// Live site turning off: (from final state) - /// - (! Restart) =True & =0 (Subscription only non notifications) - /// - (Restaret) =False + /// - (! Restart) enableTcpConnectionEndpointRediscovery=True and notificationConcurrency=0 (Subscription only non notifications) + /// - (Restaret) enableTcpConnectionEndpointRediscovery=False /// /// Monitoring: /// - TCP direct connections in TIMED_WAIT state /// - Task scheduler contention /// - internal sealed class ConnectionStateMuxListener : IConnectionStateListener -#pragma warning restore CS1584 // XML comment has syntactically incorrect cref attribute -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row -#pragma warning restore CS1570 // XML comment has badly formed XML + internal sealed class ConnectionStateMuxListener : IConnectionStateListener { readonly internal bool enableTcpConnectionEndpointRediscovery; readonly internal ConcurrentDictionary, object>> serverKeyEventHandlers = new(); @@ -61,12 +54,10 @@ internal sealed class ConnectionStateMuxListener : IConnectionStateListener public ConnectionStateMuxListener(bool enableTcpConnectionEndpointRediscovery) { -#pragma warning disable CS1587 // XML comment has badly formed XML /// Default to the processor count this.notificationConcurrency = Environment.ProcessorCount; this.notificationSemaphore = new SemaphoreSlim(this.notificationConcurrency); this.enableTcpConnectionEndpointRediscovery = enableTcpConnectionEndpointRediscovery; -#pragma warning restore CS1587 // XML comment has badly formed XML } public void SetConnectionEventConcurrency(int notificationConcurrency) diff --git a/Microsoft.Azure.Cosmos/src/direct/ConsistencyLevel.cs b/Microsoft.Azure.Cosmos/src/direct/ConsistencyLevel.cs index e890ba78a8..4dce1f3b50 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ConsistencyLevel.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ConsistencyLevel.cs @@ -24,14 +24,11 @@ enum ConsistencyLevel /// Strong, - -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -/// + /// /// Bounded Staleness guarantees that reads are not too out-of-date. This can be configured based on number of operations (MaxStalenessPrefix) /// or time (MaxStalenessIntervalInSeconds). For more information on MaxStalenessPrefix and MaxStalenessIntervalInSeconds, please see . /// BoundedStaleness, -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved /// /// Session Consistency guarantees monotonic reads (you never read old data, then new, then old again), monotonic writes (writes are ordered) @@ -51,4 +48,17 @@ enum ConsistencyLevel /// ConsistentPrefix } + + internal static class ConsistencyLevelExtensions + { + public static string ToConsistencyLevelString(this ConsistencyLevel consistencyLevel) => consistencyLevel switch + { + ConsistencyLevel.Strong => "Strong", + ConsistencyLevel.BoundedStaleness => "BoundedStaleness", + ConsistencyLevel.Session => "Session", + ConsistencyLevel.Eventual => "Eventual", + ConsistencyLevel.ConsistentPrefix => "ConsistentPrefix", + _ => "Unknown", + }; + } } diff --git a/Microsoft.Azure.Cosmos/src/direct/ConsistencyReader.cs b/Microsoft.Azure.Cosmos/src/direct/ConsistencyReader.cs index dea96653af..a34b52e5c4 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ConsistencyReader.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ConsistencyReader.cs @@ -236,7 +236,6 @@ public Task ReadAsync( { // RequestRetryUtility vs BackoffRetryUtility: is purely for safe flighting purpose only // Post flighting can be fully pivoted to RequestRetryUtility and remove BackoffRetryUtility below -#pragma warning disable SA1008 // Opening parenthesis should be spaced correctly if (entity.UseStatusCodeFor4041002 && entity.IsValidRequestFor4041002 ()) { @@ -247,7 +246,6 @@ public Task ReadAsync( sessionRetryOptions: this.sessionRetryOptions), cancellationToken: cancellationToken); } -#pragma warning restore SA1008 // Opening parenthesis should be spaced correctly return BackoffRetryUtility.ExecuteAsync( callbackMethod: () => this.ReadSessionAsync(entity, desiredReadMode), diff --git a/Microsoft.Azure.Cosmos/src/direct/ConsistencyWriter.cs b/Microsoft.Azure.Cosmos/src/direct/ConsistencyWriter.cs index 44dbbc9ae5..1bdd8e8e56 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ConsistencyWriter.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ConsistencyWriter.cs @@ -9,9 +9,12 @@ namespace Microsoft.Azure.Documents using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; + using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Core.Trace; + using static Microsoft.Azure.Documents.RntbdConstants; + /* ConsistencyWriter has two modes for writing - local quorum-acked write and globally strong write. @@ -28,11 +31,21 @@ 3. Number of read regions returned by write response > 0. Similarly, we send single request to primary of write region, which will take care of replicating to its secondaries, one of which is XPPrimary. XPPrimary will then replicate to all remote regions, which will all ack from within their region. In the write region, the request returns from the backend once write quorum number of replicas commits the write - but at this time, the response cannot be returned to caller, since linearizability guarantees will be violated. ConsistencyWriter will continuously issue barrier head requests against the partition in question, until GlobalCommittedLsn is at least as big as the lsn of the original response. 1. Issue write request to write region 2. Receive response from primary of write region, look at GlobalCommittedLsn and LSN headers. -3. If GlobalCommittedLSN == LSN, return response to caller +3. If GlobalCommittedLSN = LSN, return response to caller 4. If GlobalCommittedLSN < LSN, cache LSN in request as SelectedGlobalCommittedLSN, and issue barrier requests against any/all replicas. 5. Each barrier response will contain its own LSN and GlobalCommittedLSN, check for any response that satisfies GlobalCommittedLSN >= SelectedGlobalCommittedLSN 6. Return to caller on success. +For Less than Strong accounts with EnableNRegionSynchronousCommit feature enabled: +Business logic: We send single request to primary of the Write region,which will take care of replicating to its secondaries, one of which is XPPrimary. XPPrimary in this case will replicate this request to n read regions, which will ack from within their region. + In the write region where the original request was sent to , the request returns from the backend once write quorum number of replicas commits the write - but at this time, the response cannot be returned to caller, since linearizability guarantees will be violated. + ConsistencyWriter will continuously issue barrier head requests against the partition in question, until GlobalNRegionCommittedGLSN is at least as big as the lsn of the original response. +Sequence of steps: +1. After receiving response from primary of write region, look at GlobalNRegionCommittedGLSN and LSN headers. +2. If GlobalNRegionCommittedGLSN = LSN, return response to caller +3. If GlobalNRegionCommittedGLSN < LSN && storeResponse.NumberOFReadRegions > 0 , cache LSN in request as SelectedGlobalNRegionCommittedGLSN, and issue barrier requests against any/all replicas. +4. Each barrier response will contain its own LSN and GlobalNRegionCommittedGLSN, check for any response that satisfies GlobalNRegionCommittedGLSN >= SelectedGlobalNRegionCommittedGLSN +5. Return to caller on success. */ [SuppressMessage("", "AvoidMultiLineComments", Justification = "Multi line business logic")] @@ -54,6 +67,7 @@ internal sealed class ConsistencyWriter private readonly AddressSelector addressSelector; private readonly ISessionContainer sessionContainer; private readonly IServiceConfigurationReader serviceConfigReader; + private readonly IServiceConfigurationReaderExtension serviceConfigurationReaderExtension; private readonly IAuthorizationTokenProvider authorizationTokenProvider; private readonly bool useMultipleWriteLocations; private readonly ISessionRetryOptions sessionRetryOptions; @@ -66,13 +80,13 @@ public ConsistencyWriter( IAuthorizationTokenProvider authorizationTokenProvider, bool useMultipleWriteLocations, bool enableReplicaValidation, - AccountConfigurationProperties accountConfigurationProperties, ISessionRetryOptions sessionRetryOptions = null) { this.transportClient = transportClient; this.addressSelector = addressSelector; this.sessionContainer = sessionContainer; this.serviceConfigReader = serviceConfigReader; + this.serviceConfigurationReaderExtension = serviceConfigReader as IServiceConfigurationReaderExtension; this.authorizationTokenProvider = authorizationTokenProvider; this.useMultipleWriteLocations = useMultipleWriteLocations; this.sessionRetryOptions = sessionRetryOptions; @@ -131,6 +145,19 @@ public async Task WriteAsync( string sessionToken = entity.Headers[HttpConstants.HttpHeaders.SessionToken]; try { + // RequestRetryUtility vs BackoffRetryUtility: is purely for safe flighting purpose only + // Post flighting can be fully pivoted to RequestRetryUtility and remove BackoffRetryUtility below + if (entity.UseStatusCodeFor4041002 + && entity.IsValidRequestFor4041002()) + { + return await RequestRetryUtility.ProcessRequestAsync( + executeAsync: () => this.WritePrivateAsync(entity, timeout, forceRefresh), + prepareRequest: () => entity, + policy: new SessionTokenMismatchRetryPolicy( + sessionRetryOptions: this.sessionRetryOptions), + cancellationToken: cancellationToken); + } + return await BackoffRetryUtility.ExecuteAsync( callbackMethod: () => this.WritePrivateAsync(entity, timeout, forceRefresh), retryPolicy: new SessionTokenMismatchRetryPolicy( @@ -164,7 +191,7 @@ private async Task WritePrivateAsync( request.RequestContext.ForceRefreshAddressCache = forceRefresh; - if (request.RequestContext.GlobalStrongWriteStoreResult == null) + if (request.RequestContext.CachedWriteStoreResult == null) { StoreResponse response = null; @@ -264,74 +291,68 @@ private async Task WritePrivateAsync( throw new InternalServerErrorException(); } - if (ReplicatedResourceClient.IsGlobalStrongEnabled() && this.ShouldPerformWriteBarrierForGlobalStrong(storeResult.Target, request.OperationType)) - { - long lsn = storeResult.Target.LSN; - long globalCommittedLsn = storeResult.Target.GlobalCommittedLSN; - - if (lsn == -1 || globalCommittedLsn == -1) - { - DefaultTrace.TraceWarning("ConsistencyWriter: LSN {0} or GlobalCommittedLsn {1} is not set for global strong request", - lsn, globalCommittedLsn); - // Service Generated because no lsn and glsn set by service - throw new GoneException(RMResources.Gone, SubStatusCodes.ServerGenerated410); - } - - request.RequestContext.GlobalStrongWriteStoreResult = storeResult; - request.RequestContext.GlobalCommittedSelectedLSN = lsn; - - //if necessary we would have already refreshed cache by now. - request.RequestContext.ForceRefreshAddressCache = false; - - DefaultTrace.TraceInformation("ConsistencyWriter: globalCommittedLsn {0}, lsn {1}", globalCommittedLsn, lsn); - //barrier only if necessary, i.e. when write region completes write, but read regions have not. - if (globalCommittedLsn < lsn) - { -#pragma warning disable SA1001 // Commas should be spaced correctly - using (DocumentServiceRequest barrierRequest = await BarrierRequestHelper.CreateAsync(request, this.authorizationTokenProvider, null, request.RequestContext.GlobalCommittedSelectedLSN , includeRegionContext: true)) - { - if (!await this.WaitForWriteBarrierAsync(barrierRequest, request.RequestContext.GlobalCommittedSelectedLSN)) - { - DefaultTrace.TraceError("ConsistencyWriter: Write barrier has not been met for global strong request. SelectedGlobalCommittedLsn: {0}", request.RequestContext.GlobalCommittedSelectedLSN); - throw new GoneException(RMResources.GlobalStrongWriteBarrierNotMet, SubStatusCodes.Server_GlobalStrongWriteBarrierNotMet); - } - } -#pragma warning restore SA1001 // Commas should be spaced correctly - } - } - else + BarrierType barrierType = this.ComputeBarrierType(storeResult, request); + if (barrierType == BarrierType.None) { + // If barrier is not performed, we can return the store result directly. return storeResult.Target.ToResponse(); } + + await this.PerformBarriersForWritesAsync(storeResult, request, barrierType); } else { - using (DocumentServiceRequest barrierRequest = await BarrierRequestHelper.CreateAsync(request, this.authorizationTokenProvider, null, request.RequestContext.GlobalCommittedSelectedLSN, includeRegionContext: true)) - { - if (!await this.WaitForWriteBarrierAsync(barrierRequest, request.RequestContext.GlobalCommittedSelectedLSN)) - { - DefaultTrace.TraceWarning("ConsistencyWriter: Write barrier has not been met for global strong request. SelectedGlobalCommittedLsn: {0}", request.RequestContext.GlobalCommittedSelectedLSN); - throw new GoneException(RMResources.GlobalStrongWriteBarrierNotMet, SubStatusCodes.Server_GlobalStrongWriteBarrierNotMet); - } - } + BarrierType barrierType = this.ComputeBarrierType(request.RequestContext.CachedWriteStoreResult, request); + Func lsnAttributeSelector = barrierType == BarrierType.GlobalStrongWrite ? (sr => sr.GlobalCommittedLSN) : (sr => sr.GlobalNRegionCommittedGLSN); + + await this.CreateAndWaitForWriteBarrierAsync(request, barrierType, lsnAttributeSelector); } - return request.RequestContext.GlobalStrongWriteStoreResult.Target.ToResponse(); + return request.RequestContext.CachedWriteStoreResult.Target.ToResponse(); } - internal bool ShouldPerformWriteBarrierForGlobalStrong(StoreResult storeResult, OperationType operationType) + internal bool ShouldPerformWriteBarrierForGlobalStrong( + StoreResult storeResult, + DocumentServiceRequest incomingRequest) { - if (operationType.IsSkippedForWriteBarrier()) + #if !COSMOSCLIENT + bool skipSettingStrongConsistencyHeaderForWrites = false; + + // For write requests sending the header to skip setting strong consistency header, we should not set consistency level header. + if (bool.TryParse(incomingRequest.Headers[HttpConstants.HttpHeaders.SkipSettingStrongConsistencyHeaderForWrites], out skipSettingStrongConsistencyHeaderForWrites) && + skipSettingStrongConsistencyHeaderForWrites) + { + return false; + } + #endif + + if (incomingRequest.OperationType.IsSkippedForWriteBarrier()) { return false; } + ConsistencyLevel consistencyLevel = this.serviceConfigReader.DefaultConsistencyLevel; + if (this.serviceConfigurationReaderExtension != null && + this.serviceConfigurationReaderExtension.TryGetConsistencyLevel(incomingRequest, out ConsistencyLevel consistencyLevelOverride)) + { + // Allow overriding consistency level for specific resource type and operation type + // This is currently done for meta-data resources for PPAF enabled accounts where consistency level is overridden to Strong if account consistency is less than Strong. + // Strong consistency is crucial to avoid partial updates and ensure that operations reflect a consistent state. + DefaultTrace.TraceInformation( + "ConsistencyWriter: ConsistencyLevel is overridden from {0} to {1} for resourceType {2} and operationType {3}", + consistencyLevel, + consistencyLevelOverride, + incomingRequest.ResourceType.ToResourceTypeString(), + incomingRequest.OperationType.ToOperationTypeString()); + consistencyLevel = consistencyLevelOverride; + } + if (storeResult.StatusCode < StatusCodes.StartingErrorCode || storeResult.StatusCode == StatusCodes.Conflict || (storeResult.StatusCode == StatusCodes.NotFound && storeResult.SubStatusCode != SubStatusCodes.ReadSessionNotAvailable) || storeResult.StatusCode == StatusCodes.PreconditionFailed) { - if (this.serviceConfigReader.DefaultConsistencyLevel == ConsistencyLevel.Strong && storeResult.NumberOfReadRegions > 0) + if (consistencyLevel == ConsistencyLevel.Strong && storeResult.NumberOfReadRegions > 0) { return true; } @@ -340,29 +361,166 @@ internal bool ShouldPerformWriteBarrierForGlobalStrong(StoreResult storeResult, return false; } + /// + /// Attempt barrier requests if applicable. + /// Cases in which barrier head requests are applicable (refer to comments on the class definition for more details on the whole write protocol. + /// For globally strong write: + /// 1. After receiving response from primary of write region, look at GlobalCommittedLsn and LSN headers. + /// 2. If GlobalCommittedLSN = LSN, return response to caller + /// 3. If GlobalCommittedLSN < LSN, cache LSN in request as SelectedGlobalCommittedLSN, and issue barrier requests against any/all replicas. + /// 4. Each barrier response will contain its own LSN and GlobalCommittedLSN, check for any response that satisfies GlobalCommittedLSN >= SelectedGlobalCommittedLSN + /// 5. Return to caller on success. + /// For less than Strong Accounts and If EnableNRegionSynchronousCommit is enabled for the account: + /// 1. After receiving response from primary of write region, look at GlobalCommittedLsn and LSN headers. + /// 2. If GlobalNRegionCommittedGLSN = LSN, return response to caller + /// 3. If GlobalNRegionCommittedGLSN < LSN && storeResponse.NumberOFReadRegions > 0 , cache LSN in request as SelectedGlobalNRegionCommittedGLSN, and issue barrier requests against any/all replicas. + /// 4. Each barrier response will contain its own LSN and GlobalNRegionCommittedGLSN, check for any response that satisfies GlobalNRegionCommittedGLSN >= SelectedGlobalNRegionCommittedGLSN + /// 5. Return to caller on success. + /// + /// + /// + /// + /// + /// + private async Task PerformBarriersForWritesAsync( + ReferenceCountedDisposable storeResult, + DocumentServiceRequest request, + BarrierType barrierType) + { + long lsn = storeResult.Target.LSN; + long globalCommitLsnToBeTracked = -1; + Func lsnAttributeSelector = null; + + //No need to run any barriers. + if (barrierType == BarrierType.None) + { + return true; + } + + string warningMessage = string.Empty; + switch (barrierType) + { + case BarrierType.GlobalStrongWrite: + { + request.RequestContext.GlobalStrongWriteEndpoint = request.RequestContext.LocationEndpointToRoute; + globalCommitLsnToBeTracked = storeResult.Target.GlobalCommittedLSN; + warningMessage = "ConsistencyWriter: LSN {0} or GlobalCommittedLsn {1} is not set for global strong request"; + + DefaultTrace.TraceInformation("ConsistencyWriter: globalCommittedLsn {0}, lsn {1}", globalCommitLsnToBeTracked, lsn); + lsnAttributeSelector = sr => sr.GlobalCommittedLSN; + break; + } + + case BarrierType.NRegionSynchronousCommit: + { + globalCommitLsnToBeTracked = storeResult.Target.GlobalNRegionCommittedGLSN; + warningMessage = "ConsistencyWriter: LSN {0} or globalNRegionCommittedLsn {1} is not set for less than strong request with EnableNRegionSynchronousCommit property enabled "; + + DefaultTrace.TraceInformation("ConsistencyWriter: globalNRegionCommittedLsn {0}, lsn {1}", globalCommitLsnToBeTracked, lsn); + lsnAttributeSelector = sr => sr.GlobalNRegionCommittedGLSN; + break; + } + + default: + break; + } + + if (lsn == -1 || globalCommitLsnToBeTracked == -1) + { + DefaultTrace.TraceWarning(warningMessage, lsn, globalCommitLsnToBeTracked); + // Service Generated because no lsn and glsn set by service + throw new GoneException(RMResources.Gone, SubStatusCodes.ServerGenerated410); + } + + request.RequestContext.CachedWriteStoreResult = storeResult; + request.RequestContext.GlobalCommittedSelectedLSN = lsn; + //if necessary we would have already refreshed cache by now. + request.RequestContext.ForceRefreshAddressCache = false; + + //barrier only if necessary, i.e. when write region completes write, but read regions have not. + if (globalCommitLsnToBeTracked < lsn) + { + await this.CreateAndWaitForWriteBarrierAsync(request, barrierType, lsnAttributeSelector); + } + + return true; + } + + /// + /// Issues a barrier request and waits until the write barrier condition is met for the specified consistency level. + /// This method is used to ensure that a write operation is fully committed across the required replicas or regions + /// before returning success to the caller. If the barrier condition is not met within the allowed retries or time, + /// a GoneException is thrown to indicate the write barrier was not satisfied. + /// + private async Task CreateAndWaitForWriteBarrierAsync( + DocumentServiceRequest request, + BarrierType barrierType, + Func lsnAttributeSelector = null) + { + //No need to run any barriers. + if (barrierType == BarrierType.None) + { + return; + } + + using (DocumentServiceRequest barrierRequest = await BarrierRequestHelper.CreateAsync( + request: request, + authorizationTokenProvider: this.authorizationTokenProvider, + targetLsn: null, + targetGlobalCommittedLsn: request.RequestContext.GlobalCommittedSelectedLSN, + includeRegionContext: true)) + { + if (!await this.WaitForWriteBarrierAsync(barrierRequest, request.RequestContext.GlobalCommittedSelectedLSN, lsnAttributeSelector)) + { + if (barrierType == BarrierType.GlobalStrongWrite) + { + DefaultTrace.TraceWarning("ConsistencyWriter: Write barrier has not been met for global strong request. SelectedGlobalCommittedLsn: {0}", request.RequestContext.GlobalCommittedSelectedLSN); + throw new GoneException(RMResources.GlobalStrongWriteBarrierNotMet, SubStatusCodes.Server_GlobalStrongWriteBarrierNotMet); + } + else + { + DefaultTrace.TraceWarning("ConsistencyWriter: Write barrier has not been met for n region synchronous commit request. SelectedGlobalCommittedLsn: {0}", request.RequestContext.GlobalCommittedSelectedLSN); + throw new GoneException(RMResources.NRegionCommitWriteBarrierNotMet, SubStatusCodes.Server_NRegionCommitWriteBarrierNotMet); + } + } + } + } + + /// + /// Waits for the write barrier condition to be met by issuing barrier requests to replicas. + /// This method determines which barrier handling implementation to use (old or new) based on feature flags, + /// and delegates the actual waiting logic. It returns true if the required global committed LSN is observed + /// within the allowed retries and time, otherwise returns false. + /// private Task WaitForWriteBarrierAsync( DocumentServiceRequest barrierRequest, - long selectedGlobalCommittedLsn) + long selectedGlobalCommittedLsn, + Func lsnAttributeSelector) { if (BarrierRequestHelper.IsOldBarrierRequestHandlingEnabled) { - return this.WaitForWriteBarrierOldAsync(barrierRequest, selectedGlobalCommittedLsn); + return this.WaitForWriteBarrierOldAsync(barrierRequest, selectedGlobalCommittedLsn, lsnAttributeSelector); } - return this.WaitForWriteBarrierNewAsync(barrierRequest, selectedGlobalCommittedLsn); + return this.WaitForWriteBarrierNewAsync(barrierRequest, selectedGlobalCommittedLsn, lsnAttributeSelector); } // NOTE this is only temporarily kept to have a feature flag // (Env variable 'AZURE_COSMOS_OLD_BARRIER_REQUESTS_HANDLING_ENABLED' allowing to fall back // This old implementation will be removed (and the environment // variable not been used anymore) after some bake time. - private async Task WaitForWriteBarrierOldAsync(DocumentServiceRequest barrierRequest, long selectedGlobalCommittedLsn) + private async Task WaitForWriteBarrierOldAsync( + DocumentServiceRequest barrierRequest, + long selectedGlobalCommittedLsn, + Func lsnAttributeSelector) { int writeBarrierRetryCount = ConsistencyWriter.maxNumberOfWriteBarrierReadRetries; - + bool lastAttemptWasThrottled = false; long maxGlobalCommittedLsnReceived = 0; while (writeBarrierRetryCount-- > 0) { + this.ValidateGlobalStrongWriteEndpoint(barrierRequest); + barrierRequest.RequestContext.TimeoutHelper.ThrowTimeoutIfElapsed(); IList> responses = await this.storeReader.ReadMultipleReplicaAsync( barrierRequest, @@ -374,14 +532,39 @@ private async Task WaitForWriteBarrierOldAsync(DocumentServiceRequest barr checkMinLSN: false, forceReadAll: false); - if (responses != null && responses.Any(response => response.Target.GlobalCommittedLSN >= selectedGlobalCommittedLsn)) + if (BarrierRequestHelper.IsGoneLeaseNotFound(responses)) { - return true; + // Try primary with force refresh. If primary also fails with 410/1022, this will throw and bail out. + bool isSuccess = await this.TryPrimaryOnlyWriteBarrierAsync(barrierRequest, selectedGlobalCommittedLsn); + if (isSuccess) + { + return true; + } + } + lastAttemptWasThrottled = false; + if (responses != null && responses.Any(response => lsnAttributeSelector(response.Target) >= selectedGlobalCommittedLsn)) + { + // Check if all replicas returned 429, but don't exit early. + if (responses.Count > 0 && responses.All(response => response.Target.StatusCode == StatusCodes.TooManyRequests)) + { + DefaultTrace.TraceInformation( + "WaitForWriteBarrierOldAsync: All replicas returned 429 Too Many Requests. Continuing retries. StatusCode: {0}, SubStatusCode: {1}, PkRangeId :{2}.", + responses[0].Target.StatusCode, + responses[0].Target.SubStatusCode, + responses[0].Target.PartitionKeyRangeId); + lastAttemptWasThrottled = true; + } + + // Check if any response satisfies the barrier condition + if (responses.Any(response => response.Target.GlobalCommittedLSN >= selectedGlobalCommittedLsn)) + { + return (true); // Barrier condition met + } } //get max global committed lsn from current batch of responses, then update if greater than max of all batches. - long maxGlobalCommittedLsn = responses != null ? responses.Select(s => s.Target.GlobalCommittedLSN).DefaultIfEmpty(0).Max() : 0; - maxGlobalCommittedLsnReceived = maxGlobalCommittedLsnReceived > maxGlobalCommittedLsn ? maxGlobalCommittedLsnReceived : maxGlobalCommittedLsn; + long maxGlobalCommittedLsn = responses != null ? responses.Select(s => lsnAttributeSelector(s.Target)).DefaultIfEmpty(0).Max() : 0; + maxGlobalCommittedLsnReceived = Math.Max(maxGlobalCommittedLsnReceived, maxGlobalCommittedLsn); //only refresh on first barrier call, set to false for subsequent attempts. barrierRequest.RequestContext.ForceRefreshAddressCache = false; @@ -404,24 +587,51 @@ private async Task WaitForWriteBarrierOldAsync(DocumentServiceRequest barr } } } - + if (lastAttemptWasThrottled) + { + DefaultTrace.TraceWarning("ConsistencyWriter: Write barrier failed after all retries due to consistent throttling (429). Throwing RequestTimeoutException (408)."); + throw new RequestTimeoutException(RMResources.RequestTimeout, SubStatusCodes.Server_WriteBarrierThrottled); + } DefaultTrace.TraceInformation("ConsistencyWriter: Highest global committed lsn received for write barrier call is {0}", maxGlobalCommittedLsnReceived); - return false; + return false; // Barrier condition not met + } + + private void ValidateGlobalStrongWriteEndpoint(DocumentServiceRequest barrierRequest) + { + // validate that a regional failover has not occurred since the initial write. + Uri currentEndpoint = barrierRequest.RequestContext.LocationEndpointToRoute; + if (barrierRequest.RequestContext.GlobalStrongWriteEndpoint != null && + barrierRequest.RequestContext.GlobalStrongWriteEndpoint != currentEndpoint) + { + DefaultTrace.TraceError( + "ConsistencyWriter: Failover detected during strong consistency write. Original write was to endpoint '{0}', but retry is targeting endpoint '{1}'. Failing request.", + barrierRequest.RequestContext.GlobalStrongWriteEndpoint, + currentEndpoint); + + throw new RequestTimeoutException( + string.Format( + CultureInfo.CurrentUICulture, + "The write operation was initiated in region with endpoint '{0}' but a regional failover occurred. The current attempt is to endpoint '{1}'. The state of the write is ambiguous.", + barrierRequest.RequestContext.GlobalStrongWriteEndpoint, + currentEndpoint), SubStatusCodes.WriteRegionBarrierChangedMidOperation); + } } + private async Task WaitForWriteBarrierNewAsync( DocumentServiceRequest barrierRequest, - long selectedGlobalCommittedLsn) + long selectedGlobalCommittedLsn, + Func lsnAttributeSelector) { TimeSpan remainingDelay = totalAllowedBarrierRequestDelay; int writeBarrierRetryCount = 0; long maxGlobalCommittedLsnReceived = 0; -#pragma warning disable SA1108 + bool lastAttemptWasThrottled = false; while (writeBarrierRetryCount < defaultBarrierRequestDelays.Length && remainingDelay >= TimeSpan.Zero) // Retry loop -#pragma warning restore SA1108 { + this.ValidateGlobalStrongWriteEndpoint(barrierRequest); barrierRequest.RequestContext.TimeoutHelper.ThrowTimeoutIfElapsed(); ValueStopwatch barrierRequestStopWatch = ValueStopwatch.StartNew(); @@ -436,20 +646,42 @@ private async Task WaitForWriteBarrierNewAsync( forceReadAll: false); barrierRequestStopWatch.Stop(); + // Pivot to primary-only if any 410/1022 seen + if (BarrierRequestHelper.IsGoneLeaseNotFound(responses)) + { + bool isSuccess = await this.TryPrimaryOnlyWriteBarrierAsync(barrierRequest, selectedGlobalCommittedLsn); + if (isSuccess) return true; + + barrierRequest.RequestContext.ForceRefreshAddressCache = false; + } + TimeSpan previousBarrierRequestLatency = barrierRequestStopWatch.Elapsed; long maxGlobalCommittedLsn = 0; + lastAttemptWasThrottled = false; if (responses != null) { + // Check if all replicas returned 429, but don't exit early. + if (responses.Count > 0 && responses.All(response => response.Target.StatusCode == StatusCodes.TooManyRequests)) + { + DefaultTrace.TraceInformation( + "WaitForWriteBarrierNewAsync: All replicas returned 429 Too Many Requests. Continuing retries. StatusCode: {0}, SubStatusCode: {1}, PkRangeId :{2}.", + responses[0].Target.StatusCode, + responses[0].Target.SubStatusCode, + responses[0].Target.PartitionKeyRangeId); + lastAttemptWasThrottled = true; + } + foreach (ReferenceCountedDisposable response in responses) { - if (response.Target.GlobalCommittedLSN >= selectedGlobalCommittedLsn) + long selectedLsn = lsnAttributeSelector(response.Target); + if (selectedLsn >= selectedGlobalCommittedLsn) { - return true; + return true; // Barrier condition met } - if (response.Target.GlobalCommittedLSN >= maxGlobalCommittedLsn) + if (selectedLsn >= maxGlobalCommittedLsn) { - maxGlobalCommittedLsn = response.Target.GlobalCommittedLSN; + maxGlobalCommittedLsn = selectedLsn; } } } @@ -483,10 +715,87 @@ private async Task WaitForWriteBarrierNewAsync( remainingDelay -= delay; } } - + if (lastAttemptWasThrottled) + { + DefaultTrace.TraceWarning("ConsistencyWriter: Write barrier failed after all retries due to consistent throttling (429). Throwing RequestTimeoutException (408)."); + throw new RequestTimeoutException(RMResources.RequestTimeout, SubStatusCodes.Server_WriteBarrierThrottled ); + } DefaultTrace.TraceInformation("ConsistencyWriter: Highest global committed lsn received for write barrier call is {0}", maxGlobalCommittedLsnReceived); return false; } + + /// + /// Primary-only write-barrier check. If primary is 410/1022 we bail out (throw) to let region failover happen. + /// Adds a single forced-address-refresh retry to handle stale primary mapping in the SDK. + /// + private async Task TryPrimaryOnlyWriteBarrierAsync( + DocumentServiceRequest barrierRequest, + long selectedGlobalCommittedLsn) + { + this.ValidateGlobalStrongWriteEndpoint(barrierRequest); + + // Always force refresh before hitting primary to avoid stale primary selection + barrierRequest.RequestContext.ForceRefreshAddressCache = true; + using (ReferenceCountedDisposable primaryResult = await this.storeReader.ReadPrimaryAsync( + barrierRequest, + requiresValidLsn: false, + useSessionToken: false)) + { + if (!primaryResult.Target.IsValid || BarrierRequestHelper.IsGoneLeaseNotFound(primaryResult.Target)) + { + // Bail out: propagate the error, do not retry further + ExceptionDispatchInfo.Capture(primaryResult.Target.GetException()).Throw(); + } + + return (primaryResult.Target.GlobalCommittedLSN >= selectedGlobalCommittedLsn); + } + } + + /// + /// Determines the type of write barrier required for a given store result and request. + /// Returns BarrierType.GlobalStrongWrite if global strong consistency is enabled and required, + /// BarrierType.NRegionSynchronousCommit if N-region synchronous commit is applicable, + /// or BarrierType.None if no barrier is needed. + /// + private BarrierType ComputeBarrierType( + ReferenceCountedDisposable storeResult, + DocumentServiceRequest request) + { + if (ReplicatedResourceClient.IsGlobalStrongEnabled() && this.ShouldPerformWriteBarrierForGlobalStrong(storeResult.Target, request)) + { + return BarrierType.GlobalStrongWrite; + } + else if (this.ShouldPerformWriteBarrierForLessThanStrong(storeResult.Target)) + { + return BarrierType.NRegionSynchronousCommit; + } + + return BarrierType.None; + } + + /// + /// Determines whether a write barrier should be performed for single-master accounts with less than strong consistency + /// and the N-Region Synchronous Commit feature enabled. Returns true if the account's default consistency + /// is less than strong, the store result contains a valid GlobalNRegionCommittedGLSN, the feature is enabled, + /// and there are read regions present. + /// + internal bool ShouldPerformWriteBarrierForLessThanStrong( + StoreResult storeResult) + { + IServiceConfigurationReaderVnext serviceConfigurationReaderVNext = this.serviceConfigReader as IServiceConfigurationReaderVnext; + bool enableNRegionSynchronousCommit = false; + + if (serviceConfigurationReaderVNext != null) + { + enableNRegionSynchronousCommit = serviceConfigurationReaderVNext.EnableNRegionSynchronousCommit; + } + + return this.serviceConfigReader.DefaultConsistencyLevel != ConsistencyLevel.Strong + && storeResult.GlobalNRegionCommittedGLSN != -1 + && !this.useMultipleWriteLocations + && enableNRegionSynchronousCommit + && storeResult.NumberOfReadRegions > 0; + } } } diff --git a/Microsoft.Azure.Cosmos/src/direct/Constants.cs b/Microsoft.Azure.Cosmos/src/direct/Constants.cs index 608c384d5b..b90f5d9998 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Constants.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Constants.cs @@ -30,12 +30,10 @@ internal static class Constants public const int Max16KBTokenBufferSize = 16 * 1024; - public const int Max64KBTokenBufferSize = 64 * 1024; -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row + public const int Max64KBTokenBufferSize = 64 * 1024; - public const string FirewallAuthorization = "FirewallAuthorization"; -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row + public const string FirewallAuthorization = "FirewallAuthorization"; // As per https://docs.microsoft.com/en-us/azure/storage/common/storage-redundancy // table secondary endpoint is suffixed by -secondary. @@ -163,6 +161,28 @@ public static class ReadFeedOperationNames public const string FullFidelityChangeFeed = "ChangeFeed/FullFidelity"; } + public static class FeatureNames + { + public const string Fabric = "Fabric"; + public const string Fleet = "Fleet"; + public const string OperationManager = "OperationManager"; + public const string Throttling = "Throttling"; + } + + public static class TeamNames + { + public const string ControlPlane = "Control Plane"; + public const string FrontendPlatform = "Frontend Platform"; + public const string BackupAndRestore = "Backup And Restore"; + public const string CassandraMI = "CassandraMI"; + public const string Billing = "Billing Team"; + public const string RoutingGateway = "Routing Gateway"; + public const string DataTransfer = "DataTransfer"; + public const string Deployment = "Deployment"; + public const string CloudSolutions = "Cloud Solutions"; + public const string Elasticity = "Elasticity"; + } + public static class Properties { public const string Resource = "resource"; @@ -170,6 +190,10 @@ public static class Properties public const string CloudName = "cloudName"; public const string SubscriptionId = "subscriptionId"; public const string FabricClientEndpoints = "fabricClientEndpoints"; + public const string FeatureName = "featureName"; + public const string ConfigName = "configName"; + public const string ConfigValue = "configValue"; + public const string OwningTeam = "owningTeam"; public const string SubscriptionUsageType = "subscriptionUsageType"; public const string EnabledLocations = "enabledLocations"; public const string SubscriptionState = "subscriptionState"; @@ -196,6 +220,7 @@ public static class Properties public const string ExtendedStatus = "extendedStatus"; public const string ExtendedResult = "extendedResult"; public const string StatusCode = "statusCode"; + public const string SubStatusCode = "subStatusCode"; public const string DocumentEndpoint = "documentEndpoint"; public const string Endpoint = "endpoint"; public const string TableEndpoint = "tableEndpoint"; @@ -245,9 +270,13 @@ public static class Properties public const string ExtendedProperties = "extendedProperties"; public const string KeyKind = "keyKind"; public const string SystemKeyKind = "systemKeyKind"; + public const string SkipAccountKeysLastUsageCheck = "SkipAccountKeysLastUsageCheck"; public const string ScaleUnits = "scaleUnits"; public const string Location = "location"; public const string AccessKeyKind = "accessKeyKind"; + public const string RequestedAccessKeyKind = "requestedAccessKeyKind"; + public const string AccessKeyModificationTimestamp = "accessKeyModificationTimestamp"; + public const string AccessKeyPublicationTimestamp = "accessKeyPublicationTimestamp"; public const string ShouldBlockNextKeyRotation = "shouldBlockNextKeyRotation"; public const string EnableKeyCredentialUpdateNotification = "enableKeyCredentialUpdateNotification"; public const string Kind = "kind"; @@ -278,13 +307,19 @@ public static class Properties public const string DestinationSubscription = "destinationSubscription"; public const string TargetResourceGroup = "targetResourceGroup"; public const string ConfigurationOverrides = "configurationOverrides"; + public const string RestoreConfigurationOverrides = "restoreConfigurationOverrides"; + public const string ThroughputPoolConfigurationOverrides = "throughputPoolConfigurationOverrides"; + public const string SubscriptionCapabilitiesConfigOverrides = "subscriptionCapabilitiesConfigOverrides"; public const string Name = "name"; public const string Fqdn = "fqdn"; + public const string Fqdns = "fqdns"; public const string PublicIPAddressResourceName = "publicIPAddressName"; public const string PublicIPAddressResourceGroup = "publicIPAddressResourceGroup"; public const string VnetName = "vnetName"; public const string VnetResourceGroup = "vnetResourceGroup"; public const string Ipv4Address = "Ipv4Address"; + public const string Ipv6Address = "ipv6Address"; + public const string IsDualSLB = "isDualSLB"; public const string RoleNameSuffix = "roleNameSuffix"; public const string ReservedCname = "reservedCname"; public const string ServiceType = "serviceType"; @@ -368,14 +403,31 @@ public static class Properties public const string EnableMigrationConcurrency = "enableMigrationConcurrency"; public const string EnableMinNoOfNodesOnTargetCheck = "enableMinNoOfNodesOnTargetCheck"; public const string ExemptedSourcesFromLocationMigrationCheck = "exemptedSourcesFromLocationMigrationCheck"; + public const string RegionalPartitionMigrationConcurrency = "regionalPartitionMigrationConcurrency"; + public const string EnableRegionalPartitionMigrationConcurrency = "enableRegionalPartitionMigrationConcurrency"; + public const string EnableDefaultProvisionOnAzureTrafficManager = "enableDefaultProvisionOnAzureTrafficManager"; public const string RegionProximity = "regionProximity"; + public const string IgnoreVectorIndexCatchupFailureForPartitionMigration = "ignoreVectorIndexCatchupFailureForPartitionMigration"; + public const string OfflineStartTimestampUtc = "offlineStartTimestampUtc"; + public const string OnlineCompleteTimestampUtc = "onlineCompleteTimestampUtc"; + public const string OfflineOnlinePeriods = "offlineOnlinePeriods"; + public const string PartitionReuseEnabled = "partitionReuseEnabled"; + public const string ServiceAllocationOperationTypesToDeny = "serviceAllocationOperationTypesToDeny"; + public const string ForceDisablePhysicalCopyForMigration = "forceDisablePhysicalCopyForMigration"; + public const string EncryptedArmOboToken = "encryptedArmOboToken"; + public const string IsOBOFlagEnabled = "isOBOFlagEnabled"; + public const string ArmCorrelationRequestId = "armCorrelationRequestId"; + public const string DisableCrossSubRegionMigration = "disableCrossSubRegionMigration"; + public const string EnableFalseProgressReconciliation = "enableFalseProgressReconciliation"; public const string MasterValue = "masterValue"; public const string SecondaryValue = "secondaryValue"; public const string ArmLocation = "armLocation"; public const string SubscriptionName = "subscriptionName"; - public const string EnableNRegionSynchronousCommit = "enableNRegionSynchronousCommit"; + public const string EnableNRegionSynchronousCommit = "enableNRegionSynchronousCommit"; + + public const string TargetComputeFederationsForMigration = "targetComputeFederationsForMigration"; //Throughput Pool public const string MinThroughput = "minThroughput"; @@ -535,8 +587,13 @@ public static class Properties public const string ChangeFeedPolicy = "changeFeedPolicy"; public const string LogRetentionDuration = "retentionDuration"; + // Previous Image Retention Policy + public const string PreviousImageRetentionPolicy = "previousImageRetentionPolicy"; + //MaterializedViewDefinition public const string MaterializedViewDefinition = "materializedViewDefinition"; + public const string MaterializedViewsProperties = "materializedViewsProperties"; + public const string ThroughputBucketForBuild = "throughputBucketForBuild"; public const string AllowMaterializedViews = "allowMaterializedViews"; public const string SourceCollectionRid = "sourceCollectionRid"; public const string SourceCollectionId = "sourceCollectionId"; @@ -544,6 +601,8 @@ public static class Properties public const string ApiSpecificDefinition = "apiSpecificDefinition"; public const string AllowMaterializedViewsInCollectionDeleteRollForward = "allowMaterializedViewsInCollectionDeleteRollForward"; public const string MaterializedViews = "materializedViews"; + public const string EnsureMultiAZForMVBuilder = "ensureMultiAZForMVBuilder"; + public const string MaterializedViewStatus = "status"; //TieredStorageCollection public const string MaterializedViewContainerType = "containerType"; @@ -620,6 +679,82 @@ public static class Properties public const string ContinuousModeProperties = "continuousModeProperties"; public const string BackupPolicyMigrationState = "migrationState"; + // LTR Backup and restore properties + public const string RequestId = "requestId"; + public const string CollectionResourceIds = "collectionResourceIds"; + public const string CollectionResourceIdToBackupStatus = "collectionResourceIdToBackupStatus"; + public const string TargetStorageAccountContainerSasUrl = "targetStorageAccountContainerSasUrl"; + public const string CollectionResourceIdToBlobPrefix = "collectionResourceIdToBlobPrefix"; + public const string CollectionDetails = "collectionDetails"; + public const string BackupIdempotencyId = "backupIdempotencyId"; + public const string BackupTimestamp = "backupTimestamp"; + public const string LastBackupTimestamp = "lastBackupTimestamp"; + public const string BackupReady = "backupReady"; + public const string BackupSettings = "backupSettings"; + public const string BackupType = "backupType"; + public const string CrossRegionTransferEnabled = "crossRegionTransferEnabled"; + public const string CrossRegionDataTransfer = "crossRegionDataTransfer"; + public const string TargetLocation = "targetLocation"; + public const string SourceLocation = "sourceLocation"; + public const string RecoveryPointTimeInUTC = "recoveryPointTimeInUTC"; + public const string RecoveryPointId = "recoveryPointId"; + public const string RecoveryPointType = "recoveryPointType"; + public const string RestoreIdempotencyId = "restoreIdempotencyId"; + public const string OverallBandwidthForDataContainersInMegaBitsPerSecond = "overallBandwidthForDataContainersInMegaBitsPerSecond"; + public const string MinBandwidthPerDataContainerInMegaBitsPerSecond = "minBandwidthPerDataContainerInMegaBitsPerSecond"; + public const string BlobName = "blobName"; + public const string VersionId = "versionId"; + public const string BlobType = "blobType"; // blobType + public const string SasUri = "sasUri";// containerSasUri + public const string CommitBlobUri = "commitBlobUri"; // container commitBlobUri + public const string CommitBlobVersion = "commitBlobVersion"; // container commitBlobVersion + public const string StorageUnitName = "storageUnitName"; + public const string StorageUnitType = "storageUnitType"; + public const string StorageUnitList = "storageUnitList"; + public const string TargetDetails = "targetDetails"; + public const string PreviousRecoveryPoints = "previousRecoveryPoints"; + public const string RecoveryPoints = "recoveryPoints"; + public const string BackupMetadata = "backupMetadata"; // pitMetadata + public const string Uri = "uri"; // blobUri + public const string BlobList = "blobList"; + public const string DataSourceSizeInBytes = "dataSourceSizeInBytes"; + public const string DataTransferredInBytes = "dataTransferredInBytes"; // amount of data restored or backed up + public const string CommitProperties = "commitProperties"; + public const string SourceDetails = "sourceDetails"; + public const string StoreDetails = "storeDetails"; + public const string ObjectType = "objectType"; + public const string RestoreStatus = "restoreStatus"; + public const string RestoreTimestamp = "restoreTimestamp"; + public const string StorageAccountBackupContainerSasUrl = "storageAccountBackupContainerSasUrl"; + public const string StorageAccountBackupContainerDetails = "storageAccountBackupContainerDetails"; + public const string BlobPrefix = "blobPrefix"; + public const string Databases = "databases"; + public const string StorageContainerInfo = "storageContainerInfo"; + public const string PartitionKeyRange = "partitionKeyRange"; + public const string CollectionName = "collectionName"; + public const string NumberOfPartitions = "numberOfPartitions"; + public const string DocumentCollection = "collection"; + public const string Partitions = "partitions"; + public const string Collections = "collections"; + public const string BackupTimeSnapshotsByOwnerRid = "backupTimeSnapshotsByOwnerRid"; + public const string CollectionRid = "collectionRid"; + public const string PartitionMetadata = "partitionMetadata"; + + + public const string MaxPrimaryCopyMemoryInKB = "maxPrimaryCopyMemoryInKB"; + public const string MaxSecondaryCopyMemoryInKB = "maxSecondaryCopyMemoryInKB"; + + public static class ExternalBackupAndRestoreAPIEndpoints + { + public const string EnableLongTermProtection = "enableLongTermProtection"; + public const string DisableLongTermProtection = "disableLongTermProtection"; + public const string ValidateForLongTermProtection = "validateForLongTermProtection"; + public const string PreBackup = "preBackup"; + public const string Backup = "backup"; + public const string PreRestore = "preRestore"; + public const string Restore = "restore"; + } + // Tiering Policy public const string CollectionTieringPolicy = "tieringPolicy"; public const string CollectionTieringType = "type"; @@ -636,9 +771,11 @@ public static class Properties public const string PitrMigrationAttemptTimestamp = "pitrMigrationAttemptTimestamp"; public const string PreMigrationBackupPolicy = "preMigrationBackupPolicy"; public const string LastPitrStandardOptInTimestamp = "lastPitrStandardOptInTimestamp"; + public const string LastPitrStandard35OptInTimestamp = "lastPitrStandard35OptInTimestamp"; public const string LastPitrBasicOptInTimestamp = "lastPitrBasicOptInTimestamp"; public const string PitrMigrationStartTimestamp = "startTime"; public const string TargetPitrSku = "targetPitrSku"; + public const string OldestRestorableTimestampBasedOnPitrSkuUpdate = "oldestRestorableTimestampBasedOnPitrSkuUpdate"; // Periodic Migration public const string PeriodicMigrationState = "periodicMigrationState"; @@ -762,6 +899,7 @@ public static class Properties public const string ResourceToken = "resource"; public const string AadToken = "aad"; public const string SasToken = "sas"; + public const string MwcToken = "mwc"; public const string TokenVersion = "1.0"; public const string TokenVersionV2 = "2.0"; public const string AuthSchemaType = "type"; @@ -777,6 +915,7 @@ public static class Properties public const string FabricApplicationVersion = "fabricApplicationVersion"; public const string FabricRingCodeVersion = "fabricRingCodeVersion"; public const string FabricRingConfigVersion = "fabricRingConfigVersion"; + public const string TargetFabricApplicationVersion = "targetFabricApplicationVersion"; public const string CertificateVersion = "certificateVersion"; public const string DeploymentId = "deploymentId"; public const string DecommisionedFederations = "decommisionedFederations"; @@ -799,6 +938,7 @@ public static class Properties public const string IsAvailabilityZoneFederation = "IsAvailabilityZoneFederation"; public const string AZIndex = "AZIndex"; public const string IsFederationUnavailable = "isFederationUnavailable"; + public const string IsFederationUnderBuildout = "isFederationUnderBuildout"; public const string ServiceExtensions = "serviceExtensions"; public const string HostedServicesDescription = "hostedServicesDescription"; public const string CsesMigratedHostedServicesDescription = "csesMigratedHostedServicesDescription"; @@ -818,11 +958,22 @@ public static class Properties public const string EnableTenantProtection = "EnableTenantProtection"; public const string IsEnabledForClusterSpanning = "IsEnabledForClusterSpanning"; public const string EnablePhysicalCopyForMigration = "EnablePhysicalCopyForMigration"; + public const string EnablePhysicalCopyMigrationForMultiMaster = "EnablePhysicalCopyMigrationForMultiMaster"; + public const string EnablePhysicalCopyMigrationForLogStore = "EnablePhysicalCopyMigrationForLogStore"; + public const string EnablePhysicalCopyForAddRegion = "EnablePhysicalCopyForAddRegion"; + public const string EnablePhysicalCopyForAddRegionOnLogStoreAccount = "EnablePhysicalCopyForAddRegionOnLogStoreAccount"; + public const string EnablePhysicalCopyBasedMigrationForPPAF = "EnablePhysicalCopyBasedMigrationForPPAF"; + public const string DisablePhysicalCopyForAddRegion = "DisablePhysicalCopyForAddRegion"; + public const string DisablePhysicalCopyForAddRegionOnLogStoreAccount = "DisablePhysicalCopyForAddRegionOnLogStoreAccount"; public const string FabricManagerName = "FabricManagerName"; + public const string FederationEphemeralDiskMigrationStatus = "EphemeralDiskMigrationStatus"; // Federation settings public const string AllowReutilizationOfComputeResources = "AllowReutilizationOfComputeResources"; + //Partition Merge Configs + public const string EnablePartitionMergeForMultiMaster = "enablePartitionMergeForMultiMaster"; + //Deployment Constants public const string FabricApplicationName = "fabricApplicationName"; public const string UseMonitoredUpgrade = "useMonitoredUpgrade"; @@ -885,6 +1036,8 @@ public static class Properties public const string MsiDelegatedUserAssignedType = "DelegatedUserAssigned"; // only used for DefaultMsiProperties public const string MsiDelegatedSystemAssignedType = "DelegatedSystemAssigned"; // only used for DefaultMsiProperties + public const string EntraTokenRevocationRules = "entraTokenRevocationRules"; + // ServiceDocument Resource public const string AddressesLink = "addresses"; public const string UserReplicationPolicy = "userReplicationPolicy"; @@ -962,6 +1115,7 @@ public static class Properties public const string OfferResourceId = "offerResourceId"; public const string OfferThroughput = "offerThroughput"; public const string OfferTargetThroughput = "offerTargetThroughput"; + public const string PartitionCount = "partitionCount"; public const string BackgroundTaskMaxAllowedThroughputPercent = "BackgroundTaskMaxAllowedThroughputPercent"; public const string OfferIsRUPerMinuteThroughputEnabled = "offerIsRUPerMinuteThroughputEnabled"; public const string OfferIsAutoScaleEnabled = "offerIsAutoScaleEnabled"; @@ -986,6 +1140,7 @@ public static class Properties public const string ThroughputBuckets = "throughputBuckets"; public const string ThroughputBucketId = "id"; public const string ThroughputBucketMaxThroughputPercentage = "maxThroughputPercentage"; + public const string isDefaultBucket = "isDefaultBucket"; public const string AutopilotTier = "tier"; public const string AutopilotTargetTier = "targetTier"; @@ -1013,15 +1168,16 @@ public static class Properties public const string EnableAdaptiveRu = "enableAdaptiveRU"; public const string EnablePartitionMerge = "enablePartitionMerge"; + public const string ForceHPKLastLevelAsIdOnCreate = "forceHPKLastLevelAsIdOnCreate"; public const string EnableSystemSnapshotsForCompleteSplitOperationForSharedThroughput = "EnableSystemSnapshotsForCompleteSplitOperationForSharedThroughput"; public const string EnableSystemSnapshotsForCompleteSplitOperation = "EnableSystemSnapshotsForCompleteSplitOperation"; -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row public const string EnableBurstCapacity = "enableBurstCapacity"; -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row public const string EnableUserRateLimitingWithBursting = "enableUserRateLimitingWithBursting"; - + public const string SoftDeletionEnabled = "softDeletionEnabled"; + public const string SoftDeleteConfiguration = "softDeleteConfiguration"; + // Priority Based Execution can be enabled by setting the naming config enablePriorityBasedThrottling to true // When user sets EnablePriorityBasedExecution to true in an account PATCH request, enablePriorityBasedThrottling // is set to true in the account configuration overrrides @@ -1042,6 +1198,11 @@ public static class Properties public const string EnableGlobalRateLimiting = "enableGlobalRateLimiting"; // Cap the global budget for a partition. The value includes provisioned local budget. It cannot exceed 10K. public const string GRLTargetUtilizationPerPartition = "GRLTargetUtilizationPerPartition"; + public const string GlobalCapacityMaximumRUPerPartition = "GlobalCapacityMaximumRUPerPartition"; + public const string DisableLocalRateLimiterInGRL = "disableLocalRateLimiterInGRL"; + public const string DisableProcessLevelRateLimiterInGRL = "disableProcessLevelRateLimiterInGRL"; + public const string TargetTypeForGRL = "targetTypeForGRL"; + public const string TargetFederationEndpointForGRL = "targetFederationEndpointForGRL"; public const string IsDryRun = "isDryRun"; @@ -1081,6 +1242,7 @@ public static class Properties public const string OperationDisplayResource = "resource"; public const string OperationDisplayOperation = "operation"; public const string OperationDisplayDescription = "description"; + public const string OperationIsDataAction = "isDataAction"; public const string PartitionKey = "partitionKey"; public const string PartitionKeyRangeId = "partitionKeyRangeId"; @@ -1128,6 +1290,7 @@ public static class Properties public const string VectorDimensions = "dimensions"; public const string DistanceFunction = "distanceFunction"; public const string QuantizationByteSize = "quantizationByteSize"; + public const string QuantizerType = "quantizerType"; public const string IndexingSearchListSize = "indexingSearchListSize"; public const string VectorIndexShardKey = "vectorIndexShardKey"; // Vector search distance functions @@ -1138,6 +1301,12 @@ public static class Properties public const string Float32 = "float32"; public const string Uint8 = "uint8"; public const string Int8 = "int8"; + // Vector embedding generation + public const string EmbeddingSource = "embeddingSource"; + public const string SourcePath = "sourcePath"; + public const string AuthType = "authType"; + public const string DeploymentName = "deploymentName"; + public const string ModelName = "modelName"; // FullText Indexes public const string FullTextIndexPaths = "fullTextIndexes"; @@ -1198,6 +1367,11 @@ public static class Properties public const string IpRules = "ipRules"; public const string IpAddressOrRange = "ipAddressOrRange"; + // Property to check if incremental period backup is enabled for a global database account + public const string IncrementalBackupEnabled = "incrementalBackupEnabled"; + public const string IncrementalBackupEnablementTimestamp = "incrementalBackupEnablementTimestamp"; + public const string CancelOngoingBackupDuringCopy = "cancelOngoingBackupDuringCopy"; + // Property to check if point in time restore is enabled for a global database account public const string PitrEnabled = "pitrEnabled"; public const string PitrSku = "pitrSku"; @@ -1208,9 +1382,20 @@ public static class Properties public const string AllowSkippingOpLogFlushInRestore = "allowSkippingOpLogFlushInRestore"; public const string ContinuousBackupEnabled = "continuousBackupEnabled"; // used in DatabaseAccountHandler as part of database account response + // Property to add in the collection property to enable external backup and restore feature + public const string ExternalBackupFeatureFlags = "externalBackupFeatureFlags"; + public const string AzureBackupVaultEnabled = "azureBackupVaultEnabled"; + + // Property to check if Long Term Protection is enabled for a global database account + public const string LongTermProtectionEnabled = "longTermProtectionEnabled"; + // Property to check if FFCF Enabled by Collection Policy is Migrated to PITR public const string IsCollectionPolicyEnabledFFCFAccountMigratedToPITR = "IsCollectionPolicyEnabledFFCFAccountMigratedToPITR"; + // Enable to allow PITR migration of an FFCF enabled collection. + public const string AllowEnablingPITROnFFCFEnabledCollection = "allowEnablingPITROnFFCFEnabledCollection"; + public const string AllowChangeFeedPolicyRemovalDuringPITRMigrationOnFFCFEnabledCollection = "allowChangeFeedPolicyRemovalDuringPITRMigrationOnFFCFEnabledCollection"; + // Property to allow PITR disabling public const string AllowPeriodicMigration = "allowPeriodicMigration"; @@ -1222,29 +1407,69 @@ public static class Properties public const string EnableAnalyticalStorage = "enableAnalyticalStorage"; // Properties related to Autoscale billing improvements. - public const string EnableAutoscaleThroughputUtilizationReporting = "enableAutoscaleThroughputUtilizationReporting"; // Configuration to report throughput utilization for autoscale billing. public const string EnablePerRegionAutoscale = "enablePerRegionAutoscale"; //Configuration to enable PerRegion autoscale billing. public const string EnableThroughputUtilizationPersistence = "enableThroughputUtilizationPersistence"; // configuration to enable throughput utlization persistance for server replica public const string EnablePerRegionPerPartitionAutoscale = "enablePerRegionPerPartitionAutoscale"; // Property used in RP call to enable/disable the AutoscalePerRegion feature public const string EnablePerRegionPerPartitionAutoscaleOptIn = "enablePerRegionPerPartitionAutoscaleOptIn"; // config override which will be used to determine if feature can be enabled or not + // Properties related to consumption based billimg + public const string EnableConsumptionBasedBilling = "enableConsumptionBasedBilling"; // Configuration to enable Consumption Based Billing. + public const string BlockUserOfferUpdateOperationsForConsumptionBasedBilling = "blockUserOfferUpdateOperationsForConsumptionBasedBilling"; // Configuration to block user offer updates for Consumption Based Billing. + + // Properties related to Collection Level Billing + public const string EnableCollectionLevelBilling = "enableCollectionLevelBilling"; + //properties to enable MaterializedViews public const string EnableMaterializedViews = "enableMaterializedViews"; //at DB account level. public const string EnableOnlyColdStorageContainersInAccountV1 = "enableOnlyColdStorageContainersInAccountV1"; public const string EnableMaterializedViewsBuilderProvisioning = "enableMaterializedViewsBuilderProvisioning"; + // properties related to Materialized Views restore + public const string IncludeMaterializedViewsDuringAccountRestore = "IncludeMaterializedViewsDuringAccountRestore"; + public const string FailRestoreWorkflowOnMaterializedViewsRestoreFailure = "FailRestoreWorkflowOnMaterializedViewsRestoreFailure"; + public const bool FailRestoreWorkflowOnMaterializedViewsRestoreFailureDefaultValue = true; + public const string MaxWaitTimeForMVCatchupOperationDuringRestoreInHours = "MaxWaitTimeForMVCatchupOperationDuringRestoreInHours"; + public const int MaxWaitTimeForMVCatchupOperationDuringRestoreInHoursDefaultValue = 24; + public const string MaterializedViewRestoreWaitPollingIntervalInMinutes = "MaterializedViewRestoreWaitPollingIntervalInMinutes"; + public const int MaterializedViewRestoreWaitPollingIntervalInMinutesDefaultValue = 1; + public const string AllowIndefiniteRetriesDuringMaterializedViewRestoreOperation = "AllowIndefiniteRetriesDuringMaterializedViewRestoreOperation"; + public const string MaxRetriesForMaterializedViewsCatchUpRestoreIdempotentOperation = "MaxRetriesForMaterializedViewsCatchUpRestoreIdempotentOperation"; + public const string MaxRetryTimeInHoursForIndefiniteRetriesForMaterializedViewCatchUpRestoreOperation = "MaxRetryTimeInHoursForIndefiniteRetriesForMaterializedViewCatchUpRestoreOperation"; + public const string MaterializedViewRestoreCriticalTraceAlertDelayInHours = "MaterializedViewRestoreCriticalTraceAlertDelayInHours"; + public const int MaterializedViewRestoreCriticalTraceAlertDelayInHoursDefaultValue = 4; + public const string MaterializedViewRestoreCollectionReadExceptionRetryInMilliseconds = "MaterializedViewRestoreCollectionReadExceptionRetryInMilliseconds"; + public const int MaterializedViewRestoreCollectionReadExceptionRetryInMillisecondsDefaultValue = 500; + //properties to enable ChangeCapacityMode public const string EnableChangeCapacityMode = "enableChangeCapacityMode"; public const string LogFlushInterval = "logFlushInterval"; + public const string LogFlushIntervalForServerlessCapability = "10"; public const string BatchAcknowledgementIntervalMilliseconds = "batchAcknowledgementIntervalMilliseconds"; + public const string BatchAcknowledgementIntervalMillisecondsForServerlessCapability = "10"; public const string MaxCollections = "maxCollections"; + public const string MaxCollectionsForServerlessCapability = "maxCollectionsForServerlessCapability"; + public const string MaxCollectionsForServerlessCapabilityDefaultValue = "500"; public const string NamingConfigRefreshIntervalInSeconds = "namingConfigRefreshIntervalInSeconds"; public const string MinCapacityModeMigrationIntervalInHours = "minCapacityModeMigrationIntervalInHours"; public const double MinCapacityModeMigrationIntervalInHoursDefaultValue = 168; // 1 week - - // Account property to enable operations during offline workflow + public const string AllowUnsupportedThroughputFeaturesWithServerless = "allowUnsupportedThroughputFeaturesWithServerless"; + public const string AllowMaxConfiguredRUPerPartitionForServerless = "allowMaxConfiguredRUPerPartitionForServerless"; + public const string MaxOfferReplaceRetriesDuringCapacityModeChange = "maxOfferReplaceRetriesDuringCapacityModeChange"; + public const string DelayBetweenRetriesForOfferReplaceDuringCapacityModeChangeInSeconds = "delayBetweenRetriesForOfferReplaceDuringCapacityModeChangeInSeconds"; + public const string OfferReplaceDelayDuringCapacityModeChangeInMilliseconds = "offerReplaceDelayDuringCapacityModeChangeInMilliseconds"; + public const string DisableOfferReplaceForProvisionedToServerless = "disableOfferReplaceForProvisionedToServerless"; + public const string DisableOfferMigrationToAutoscaleDuringServerlessToProvisioned = "disableOfferMigrationToAutoscaleDuringServerlessToProvisioned"; + public const int DefaultMaxOfferReplaceRetriesDuringCapacityModeChange = 3; + public const int DefaultDelayBetweenRetriesForOfferReplaceDuringCapacityModeChangeInSeconds = 20; + public const int DefaultOfferReplaceDelayDuringCapacityModeChangeInMilliseconds = 1000; + public const string CapacityModeMigrationState = "capacityModeMigrationState"; + + // Account property to enable operations during offline workflow public const string EnableAccountOperationsDuringOutage = "EnableAccountOperationsDuringOutage"; + // Container Copy + public const string EnableOnlineContainerCopy = "EnableOnlineContainerCopy"; + // full fidelity change feed (change feed with retention from remote+local storage) enablement + optimizations. public const string EnableFullFidelityChangeFeed = "enableFullFidelityChangeFeed"; public const string EnableFFCFWithPITR = "enableFFCFWithPITR"; @@ -1252,18 +1477,23 @@ public static class Properties public const string EnableAllVersionsAndDeletesChangeFeed = "enableAllVersionsAndDeletesChangeFeed"; public const string DisableAdvanceLSNWhenFirstBatchIsPreempted = "disableAdvanceLSNWhenFirstBatchIsPreempted"; public const string DisableChangeFeedPKFilteringOptimizations = "disableChangeFeedPKFilteringOptimizations"; + public const string EnableFFCFStartTimeSearch = "enableFFCFStartTimeSearch"; // full fidelity change feed split handling on compute public const string EnableFullFidelityChangeFeedSplitHandling = "EnableFullFidelityChangeFeedSplitHandling"; public const string EnableFFCFPartitionSplitArchivalCaching = "enableFFCFPartitionSplitArchivalCaching"; - //Reject Invalid Consistency Level Request + // Reject Invalid Consistency Level Request public static readonly string RejectInvalidConsistencyLevelRequests = "rejectInvalidConsistencyLevelRequests"; + public const string GeoStorageAccountNames = "GeoStorageAccountNames"; + public const string EnableGeoBackup = "enableGeoBackup"; + public const string GeoLocation = "geoLocation"; + public const string GeoBackupEnableTimestamp = "geoBackupEnableTimestamp"; + public const string GeoBackupDisableTimestamp = "GeoBackupDisableTimestamp"; + // Enable API type check -#pragma warning disable SA1203 // Constants should appear before fields public const string EnableApiTypeCheck = "enableApiTypeCheck"; -#pragma warning restore SA1203 // Constants should appear before fields public const string EnabledApiTypesOverride = "enabledApiTypesOverride"; public const string ForceDisableFailover = "forceDisableFailover"; @@ -1418,6 +1648,7 @@ public static class Properties public const string DisableRemoveRegionThrottling = "disableRemoveRegionThrottling"; public const string AnalyticsStorageAccountCount = "analyticsStorageAccountCount"; public const string LibrariesFileShareQuotaInGB = "librariesFileShareQuotaInGB"; + public const string DatabaseAccountMaxPrivateEndpointCount = "databaseAccountMaxPrivateEndpointCount"; // Policy Overrides public const string DefaultSubscriptionPolicySet = "defaultSubscriptionPolicySet"; @@ -1441,7 +1672,18 @@ public static class Properties // PerPartitionFailover Flags // Likely Customer Facing public const string EnablePerPartitionFailoverBehavior = "enablePerPartitionFailoverBehavior"; + public const string PerPartitionAutomaticFailoverEnabled = "perPartitionAutomaticFailoverEnabled"; + + // Feature flags for Add/Remove region support for PPAF enabled accounts public const string AllowAddRemoveRegionWithPerPartitionFailover = "allowAddRemoveRegionWithPerPartitionFailover"; + public const string AllowAddRegionWithPerPartitionFailover = "allowAddRegionWithPerPartitionFailover"; + public const string AllowRemoveRegionWithPerPartitionFailover = "allowRemoveRegionWithPerPartitionFailover"; + + public const string EnableAddRegionMaxPartitionConcurrencyForWaitForCatchup = "enableAddRegionMaxPartitionConcurrencyForWaitForCatchup"; + public const string ShouldWaitForCatchupAfterEveryBatch = "shouldWaitForCatchupAfterEveryBatch"; + public const string UseBatchedGatewayWorkflowForAddRegionServiceReservation = "useBatchedGatewayWorkflowForAddRegionServiceReservation"; + + public const string UsePerPartitionServerServiceAddOperationUsingGatewayAtManagementLocation = "usePerPartitionServerServiceAddOperationUsingGatewayAtManagementLocation"; // Backend public const string AccountVNETFilterEnabled = "accountVNETFilterEnabled"; @@ -1681,15 +1923,19 @@ public static class Properties public const string IsOwnedByExternalProvider = "isOwnedByExternalProvider"; public const string ConnectionInformation = "connectionInformation"; public const string CrossSubRegionMigrationInProgress = "CrossSubRegionMigrationInProgress"; + public const string EnableCrossSubRegionMigrationForPrivateEndpointEnabledAccount = "EnableCrossSubRegionMigrationForPrivateEndpointEnabledAccount"; public const string AzMigrationInProgress = "AzMigrationInProgress"; public const string BypassAzMigrationDedicatedStorageAccountRedundancyCheck = "BypassAzMigrationDedicatedStorageAccountRedundancyCheck"; public const string StorageAccountMigration = "storageAccountMigration"; public const string IsPointReadInMigrationsEnabled = "IsPointReadInMigrationsEnabled"; public const string EnableListPartitionsFromServerDuringMigration = "EnableListPartitionsFromServerDuringMigration"; + public const string EnabledReseedForCMKRevokedAccountIfRequired = "EnabledReseedForCMKRevokedAccountIfRequired"; public const string DisableDeletions = "disableDeletions"; public const string DisableSDPConfigRollout = "DisableSDPConfigRollout"; public const string SkipAddRegionValidateDocumentCount = "skipAddRegionValidateDocumentCount"; public const string EnableConsolidatePrivateEndpointDataPlaneUpdates = "enableConsolidatePrivateEndpointDataPlaneUpdates"; + public const string SerializedLargePropertiesMap = "SerializedLargePropertiesMap"; + public const string AddRegionShouldSkipPEAndRegionEnablement = "addRegionShouldSkipPEAndRegionEnablement"; // Data Plane Operation Policy public const string DisableKeyBasedMetadataWriteAccess = "disableKeyBasedMetadataWriteAccess"; @@ -1753,6 +1999,10 @@ public static class Properties public const string AllowExtendedRbacActions = "AllowExtendedRbacActions"; public const string AllowReadAnalyticsRbacAction = "AllowReadAnalyticsRbacAction"; + // Azure RBAC flags + public const string EnableAzureRbac = "enableAzureRbac"; + public const string DisableLegacyRbac = "disableLegacyRbac"; + // Diagnostics settings public const string EnableControlPlaneRequestsTrace = "enableControlPlaneRequestsTrace"; public const string AtpEnableControlPlaneRequestsTrace = "atpEnableControlPlaneRequestsTrace"; @@ -1821,6 +2071,10 @@ public static class Properties public const string DisableLocalAuth = "disableLocalAuth"; public const string SignAllAadTokenReadMetadataRequests = "signAllAadTokenReadMetadataRequests"; + // APPKI Certificate Validation + public const string AppkiAcl = "appkiAcl"; + public const string ReadOnlyAppkiAcl = "readOnlyAppkiAcl"; + //Logging properties public const string DiagnosticLogSettings = "diagnosticLogSettings"; public const string EnableFullTextQuery = "enableFullTextQuery"; @@ -1886,11 +2140,37 @@ public static class Properties // This is read by Compute. public const string EnablePartitionLevelFailover = "enablePartitionLevelFailover"; + // This is read by Backend. public const string EnablePerPartitionAutomaticFailover = "enablePerPartitionAutomaticFailover"; public const string EnableFailoverManager = "enableFailoverManager"; public const string EnablePopulatingGlobalEpochTable = "enablePopulatingGlobalEpochTable"; public const string AutomaticFailoverProperties = "automaticFailoverProperties"; + public const string EnableMigrationsOnPPAFEnableAccounts = "EnableMigrationsOnPPAFEnableAccounts"; + public const string EnableCredentialStore = "enableCredentialStore"; + public const string FailoverManagerPolicyConfigJson = "failoverManagerPolicyConfigJson"; + public const string DynamicBackupLsnDiff = "dynamicBackupLsnDiff"; + public const string DynamicBackupTimeDiffInSeconds = "dynamicBackupTimeDiffInSeconds"; + public const string DynamicRidRangeAllocationStrategy = "dynamicRidRangeAllocationStrategy"; + public const string EnableAddRemoveRegionForPPAF = "enableAddRemoveRegionForPPAF"; + public const string EnableFailoverManagerInPlacePriorityListUpdate = "enableFailoverManagerInPlacePriorityListUpdate"; + public const string EnableFalseProgressDetectionUsingGlobalEpochTable = "enableFalseProgressDetectionUsingGlobalEpochTable"; + public const string EnablePopulatingUninitializedRidRange = "enablePopulatingUninitializedRidRange"; + public const string EnableRALSNComputation = "enableRALSNComputation"; + public const string EnableReconcileXPFalseProgressDocuments = "enableReconcileXPFalseProgressDocuments"; + public const string EnableResumableInterruptedLogicalCopy = "enableResumableInterruptedLogicalCopy"; + public const string EnableRidRangeIntegrationWithPerPartitionAutomaticFailover = "enableRidRangeIntegrationWithPerPartitionAutomaticFailover"; + public const string EnableXPQueuefullOptimizations = "enableXPQueuefullOptimizations"; + public const string IsSetMinGCNOnChangeRoleToPrimaryEnabled = "isSetMinGCNOnChangeRoleToPrimaryEnabled"; + public const string MinimumNumberOfRegionsRequiredToCommitWrites = "minimumNumberOfRegionsRequiredToCommitWrites"; + public const string UsePruneProgressForDocumentForIndexRecovery = "usePruneProgressForDocumentForIndexRecovery"; + public const string UseRALSNForGCLSNComputationNonStrong = "useRALSNForGCLSNComputationNonStrong"; + public const string ValidateXPControlMessageInWriteRegionWithSameGCN = "validateXPControlMessageInWriteRegionWithSameGCN"; + public const string XpAddressResolutionFailureFaultCount = "xpAddressResolutionFailureFaultCount"; + public const string XpAddressResolutionFailureFaultDurationInMin = "xpAddressResolutionFailureFaultDurationInMin"; + public const string XpCatchupConfigurationEnabled = "xpCatchupConfigurationEnabled"; + public const string XpCatchupWaitForMinQuorumEnabled = "xpCatchupWaitForMinQuorumEnabled"; + public const string EnableDynamicQuorumForPerPartitionAutomaticFailover = "enableDynamicQuorumForPerPartitionAutomaticFailover"; // Capacity settings properties public const string Capacity = "capacity"; @@ -1962,6 +2242,9 @@ public static class Properties public const string ThroughputPoolAccountResourceIdentifier = "accountResourceIdentifier"; public const string ThroughputPoolsArmResouceType = CosmosResourceProvider + "/" + ThroughputPools; public const string BlockGlobalDatabaseOperationsForGRLAcccounts = "BlockGlobalDatabaseOperationsForGRLAcccounts"; + public const string ThroughputpoolDedicatedRUs = "ThroughputpoolDedicatedRUs"; + public const string ThroughputpoolMaxConsumableRUs = "ThroughputpoolMaxConsumableRUs"; + public const string RefreshAccountThroughputPoolInfo = "RefreshAccountThroughputPoolInfo"; // Migration feature flag public const string UseOptimizedLockAcquisitionStepsDuringMigratePartition = "useOptimizedLockAcquisitionStepsDuringMigratePartition"; @@ -1975,8 +2258,13 @@ public static class Properties public const string ThinClientReadLocations = "thinClientReadLocations"; public const string ThinClientRegionName = "thinClientRegionName"; public const string ThinClientDatabaseAccountEndpoint = "thinClientDatabaseAccountEndpoint"; + + // Fault Tolerant Document Store public const string FaultTolerantDocumentStoreId = "faultTolerantDocumentStoreId"; public const string AcceptorStoreKeyId = "acceptorStoreKeyId"; + public const string KeySecretCategory = "keySecretCategory"; + public const string EncryptedAccountInactiveKey = "EncryptedAccountInactiveKey"; + public const string InactiveKeyKind = "inactiveKeyKind"; // Fabric integration properties public const string ManagedResourceName = "managedResourceName"; @@ -1992,13 +2280,21 @@ public static class Properties public const string MappedDatabaseAccountWithChildResourcesInfo = "mappedDatabaseAccountWithChildResourcesInfo"; public const string WorkspaceHasEmptyAccountSinceTimestamp = "workspaceHasEmptyAccountSinceTimestamp"; - // StorageNsp Properties + // NSP Properties + public const string AccessRuleName = "accessRuleName"; + public const string AccessRuleSubscriptionIds = "accessRuleSubscriptionIds"; public const string DefaultProfile = "defaultProfile"; public const string DefaultInboundAccessRule = "defaultInboundAccessRule"; + public const string DefaultOutboundAccessRule = "defaultOutboundAccessRule"; + public const string IpAddress = "ipAddress"; + public const string ServiceTagNames = "serviceTagNames"; public const string StorageNsp = "storagensp"; public const string StorageNspProfileName = "storageNspProfileName"; public const string StorageNspAccessRuleDirectionInbound = "Inbound"; + public const string StorageNspAccessRuleDirectionOutbound = "Outbound"; public const string StorageNspProvider = "Microsoft.Storage/storageAccounts"; + public const string StorageNspResourceType = "storageNspResourceType"; + public const string StorageNspNameSuffix = "storageNspNameSuffix"; // GlobalNspEntity Properties public const string NspName = "nspName"; @@ -2012,8 +2308,25 @@ public static class Properties public const string TotalPartitionCount = "totalPartitionCount"; public const string AvailableStoragePercentage = "availableStoragePercentage"; public const string TotalStorageInGB = "totalStorageInGB"; + public const string TotalCappedStorageInGB = "totalCappedStorageInGB"; + public const string TotalCappedPartitionCount = "totalCappedPartitionCount"; public const string SubRegionLoadMetadataList = "subRegionLoadMetadataList"; public const string ExcludeAccountFromPartitionRebalancer = "excludeFromPartitionRebalance"; + public const string EnableHybridAllocationForNonAzAccounts = "enableHybridAllocationForNonAzAccounts"; + public const string IncludeMasterLoad = "includeMasterLoad"; + + // Partition optimized placement hint + public const string ParitionOptimizedPlacementHint = "PARTITION_OPTIMIZED"; + public const string EnableServerlessCapability = "EnableServerless"; + public const string EnablePlacingServerlessPartitionsInPOFederation = "enablePlacingServerlessPartitionsInPOFederation"; + + // Private endpoint optimizations + public const string CheckForPrivateEndpointExistsForRegion = "CheckForPrivateEndpointExistsForRegion"; + public const string EnablePrivateEndpointOptimization = "EnablePrivateEndpointOptimization"; + public const string DisablePrivateEndpointServerFanout = "DisablePrivateEndpointServerFanout"; + + // Disables building the LinkIdMap for Private Endpoint backend database account resource + public const string PrivateEndpointDisableLinkIdMap = "PrivateEndpointDisableLinkIdMap"; public static class FederationOperations { @@ -2023,6 +2336,8 @@ public static class FederationOperations public static class Special { public const string EnableBinaryEncodingOfContent = "__enableBinaryEncodingOfContent"; + public const string EnableUserStringDictionaryEncoding = "__enableUserStringDictionaryEncoding"; + public const string EnableStringDictionaryPopulation = "__enableStringDictionaryPopulation"; } } @@ -2124,6 +2439,7 @@ public static class InternalIndexingProperties public const string IndexUniquifierId = "indexUniquifierId"; public const string CellExpiryIndexingMethod = "cellExpiryIndexingMethod"; public const string EnableIndexingEffectivePartitionKey = "enableIndexingEffectivePartitionKey"; + public const string EnableQuantizedSpaceOnlyDiskANNIndexBuild = "enableQuantizedSpaceOnlyDiskANNIndexBuild"; public static class UnifiedIndexInternalMetaData { public const string PropertyName = "unifiedIndexInternalMetaData"; @@ -2133,18 +2449,26 @@ public static class UnifiedIndexInternalMetaData public const string NumOfPrefixesAssignedSoFar = "numofPrefixesAssignedSoFar"; public const string IndexSlotAssigned = "nIndexSlotAssigned"; } + + public static class VectorIndexInternalMetadata + { + public const string PropertyName = "vectorIndexInternalMetadata"; + public const string EnableQuantizedSpaceOnlyDiskANNIndexBuild = "enableQuantizedSpaceOnlyDiskANNIndexBuild"; + } } public static class GatewayTransactionMetadata { public const string GatewayTransactionId = "gatewayTransactionId"; - public const string TelemetryTimestamp = "telemetryTimestamp"; + public const string TelemetryTimestamp = "telemetryTimestamp"; } public static class InternalStoreProperties { public const string PropertyName = "internalStoreProperties"; public const string EnableBinaryEncodingOfContent = "enableBinaryEncodingOfContent"; + public const string EnableUserStringDictionaryEncoding = "enableUserStringDictionaryEncoding"; + public const string EnableStringDictionaryPopulation = "enableStringDictionaryPopulation"; } public static class TypeSystemPolicy @@ -2363,6 +2687,7 @@ public static class BackupConstants public const int PitrDefaultBackupIntervalInHours = 168; // Weekly full backups public const int PitrDefaultBackupRetentionIntervalInDays = 30; public const int PitrBasicDefaultBackupRetentionIntervalInDays = 7; + public const int Pitr35DefaultBackupRetentionIntervalInDays = 35; public const int DisableBackupHoldFeature = -1; public const string BackupHoldIndefinite = "0"; @@ -2383,6 +2708,8 @@ public static class BackupConstants public const int InvalidPeriodicBackupRetention = -1; public const string BackupEndTimestamp = "backupEndTimestamp"; + + public const string LogicalSnapshotContainerPrefix = "logicalsnapshot-"; } public static class KeyVaultProperties @@ -2403,11 +2730,12 @@ public static class SoftDeletionMetadataProperties public const string SoftDeletionMetadata = "softDeletionMetadata"; public const string SerializedSoftDeletionMetadata = "serializedSoftDeletionMetadata"; public const string IsSoftDeleted = "isSoftDeleted"; - public const string SoftDeletionStartTimestampUtc = "softDeletionStartTimestampUtc"; - public const string SoftDeletionResourceExpirationTimestampUtc = "softDeletionResourceExpirationTimestampUtc"; + public const string SoftDeletionStartTimestamp = "softDeletionStartTimestamp"; + public const string SoftDeletionResourceExpirationTimestamp = "softDeletionResourceExpirationTimestamp"; public const string SoftDeletionEnabled = "softDeletionEnabled"; public const string SoftDeleteRetentionPeriodInMinutes = "softDeleteRetentionPeriodInMinutes"; public const string MinMinutesBeforePermanentDeletionAllowed = "minMinutesBeforePermanentDeletionAllowed"; + public const string DisableSoftDeleteOfReadRegions = "disableSoftDeleteOfReadRegions"; } public static class ManagedServiceIdentityProperties @@ -2428,6 +2756,10 @@ public static class ManagedServiceIdentityProperties public const string FederatedClientId = "&FederatedClientId="; public const string SourceInternalId = "&SourceInternalId="; + + // for KBEF migration of "msiClientSecretEncrypted". + public const string EnableSkipMsiClientSecretEncryption = "enableSkipMsiClientSecretEncryption"; + public const string IsMsiClientSecretEncryptionSkipped = "isMsiClientSecretEncryptionSkipped"; } public static class LogStoreConstants @@ -2488,12 +2820,15 @@ public static class PolicyConstants public const string BalancingThreshold = "balancingThreshold"; public const string TriggerThreshold = "triggerThreshold"; public const string MetricId = "metricId"; + public const string MaxAveragePartitionSizeInGB = "maxAveragePartitionSizeInGB"; + public const string MinAveragePartitionSizeInGB = "minAveragePartitionSizeInGB"; public const string MetricValue = "metricValue"; public const string MetricWeight = "metricWeight"; public const string GlobalPolicyPartitionKey = "global"; public const string Policies = "policies"; public const string FederationListSortedByUsageScore = "federationListSortedByUsageScore"; public const string ServiceAllocationOperationType = "serviceAllocationOperationType"; + public const string ServiceAllocationOperationSubType = "serviceAllocationOperationSubType"; public const string MaxAllowedPartitionCount = "maxAllowedPartitionCount"; public const string DisAllowedSubscriptionOfferKinds = "disAllowedSubscriptionOfferKinds"; public const string NumberOfFederationsToRandomize = "numberOfFederationsToRandomize"; @@ -2511,6 +2846,7 @@ public static class PolicyConstants public const string AccountPlacementRankingToExclude = "accountPlacementRankingToExclude"; public const string SubscriptionIdsToExclude = "subscriptionIdsToExclude"; public const string RegionalAccountNamesToExclude = "regionalAccountNamesToExclude"; + public const string BlockedSubscriptions = "blockedSubscriptions"; } public static class SystemStoreConstants @@ -2615,6 +2951,12 @@ public static class ReseedPartitionParameters public static string IsSharedThroughputPartition = "isSharedThroughputPartition"; } + public static class PLB + { + public const string ServiceBasedLoadBalancingType = "PartitionBased"; + public const string StorageBasedLoadBalancingType = "StorageBased"; + } + public static class MigratePartitionCallerSource { public static string Test = "Test"; @@ -2632,10 +2974,9 @@ public static class MigratePartitionCallerSource public static string FederationBuildout_CanaryAccountMigration = "FederationBuildout_CanaryAccountMigration"; public static string USER_InPlaceAZMigration = "USER_InPlaceAZMigration"; public static string ACIS_MigrateMasterBetweenZonalAndRegional = "ACIS_MigrateMasterBetweenZonalAndRegional"; -#pragma warning disable SA1310 // Field names should not contain underscore public static string Automigrator_Decommissioning = "Automigrator_Decommissioning"; -#pragma warning restore SA1310 // Field names should not contain underscore public static string LBaaS = "LBaaS_{0}"; + public static string DefaultExemptedSources = "ACIS_Manual,PLB_StorageBased_localRegion,PLB_StorageBased_CrossRegion,USER_InPlaceAZMigration,ACIS_CrossSubregionAccountMigration,LBaaS_Rebalancer_ThreeAzToTwoAz,LBaaS_Rebalancer_TwoAzToRegional"; } public static class EnvironmentVariables @@ -2705,6 +3046,11 @@ public static class ChaosConstant public const string MaxAllowedImpactedPartitionCountConstant = "MaxAllowedImpactedPartitionCount"; public const string SupportedConsistencyLevelsConstant = "SupportedConsistencyLevels"; public const string SupportedRegionsForInjectingFaultsConstant = "SupportedRegionsForInjectingFaultsConstant"; + public const string AvailabilityZone = "AvailabilityZone"; + public const string ResourceGroupName = "ResourceGroupName"; + public const string VmssScaleSetName = "VmssScaleSetName"; + public const string CustomerSubscriptionId = "CustomerSubscriptionId"; + public const string ResourceSubscriptionId = "ResourceSubscriptionId"; } public static class RegionOutageEntityConstants @@ -2718,19 +3064,24 @@ public static class FleetConstants public const string FleetName = "fleetName"; public const string FleetLocation = "fleetLocation"; public const string FleetspaceName = "fleetspaceName"; + public const string FleetResourceId = "fleetResourceId"; public const string FleetspaceResourceId = "fleetspaceResourceId"; public const string FleetInstanceId = "fleetInstanceId"; public const string IsThroughputPoolingEnabled = "isThroughputPoolingEnabled"; public const string FleetspaceApiKind = "fleetspaceApiKind"; - public const string FleetUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/fleets/{2}"; - public const string FleetspaceUriTemplate = "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/fleets/{2}/fleetspaces/{3}"; + public const string FleetUriTemplate = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/fleets/{2}"; + public const string FleetspaceUriTemplate = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/fleets/{2}/fleetspaces/{3}"; public const string Fleets = "fleets"; public const string FleetArmResourceType = Properties.CosmosResourceProvider + "/" + Fleets; - public const string GlobalDatabaseAccountFleetResponse = "globalDatabaseAccountFleetResponse"; public const string ServiceTier = "serviceTier"; public const string StorageLocationType = "storageLocationType"; public const string StorageLocationUri = "storageLocationUri"; + public const string StorageLocationTenantId = "storageLocationTenantId"; + public const string StorageAccount = "StorageAccount"; + public const string StorageLocationAuthStatus = "storageLocationAuthStatus"; + public const string StorageLocationAuthErrorCode = "storageLocationAuthErrorCode"; + public const string StorageLocationAuthInfo = "storageLocationAuthInfo"; } public static class BackgroundTaskNames @@ -2750,14 +3101,11 @@ public static class BackgroundTaskProperties public static class AccountPlacementProperties { public const string AccountPlacementRanking = "AccountPlacementRanking"; + public const string IsHybridAzEnabled = "IsHybridAzEnabled"; } -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row -#pragma warning disable SA1025 // Code should not contain multiple whitespace in a row - public static class RegionProximity -#pragma warning restore SA1025 // Code should not contain multiple whitespace in a row -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row + public static class RegionProximity { public const string ConfigurationIdPrefix = "regionproximity_"; public const string SourceRegionQueryParam = "regionproximitysourceregion"; @@ -2768,5 +3116,47 @@ public static class SDPConfigRolloutProperties public const string DatabaseAccount = "databaseAccount"; public const string SDPConfigRolloutName = "sdpConfigRolloutName"; } + + public static class DeleteReverseLeakedPartitionProperties + { + public const string WriteRegionDatabaseAccount = "writeRegionDatabaseAccount"; + public const string WriteRegionServerServiceUris = "writeRegionServerServiceUris"; + public const string WriteRegionMasterFederationId = "writeRegionMasterFederationId"; + } + + public const string SkipUnnecessaryCustomCertificateValidation = "skipUnnecessaryCustomCertificateValidation"; + + public static class KeysLastUsageProperties + { + public const string FlushKeyUsageManagerTaskIntervalInSeconds = "flushKeyUsageManagerTaskIntervalInSeconds"; + public const string PrimaryKeyValueInBackend = "0"; + public const string SecondaryKeyValueInBackend = "1"; + public const string PrimaryReadOnlyValueInBackend = "2"; + public const string SecondaryReadOnlyValueInBackend = "3"; + public const string DelegatedKeyValueInBackend = "5"; + public const int DefaultAccountKeysLastUsageThresholdInHours = 12; + public const int DefaultUploadEntitiesMaxDegreeOfTaskParallelism = 10; + public const int DefaultUploadKeysEntitiesIgnoreWindowInMinutes = 10; + } + + public static class MutualTlsConfigurations + { + public const char KeyValueDelimiter = ':'; + public const char OnlyValuesDelimiter = ';'; + public const string GeneralDelimiter = ","; + } + + public const string EnableMultiHashEffectivePartitionKeyPadding = "enableMultiHashEffectivePartitionKeyPadding"; + + public static class DistributedTransactionProperties + { + public const string EnableDistributedTransactionSupport = "enableDistributedTransactionSupport"; + } + + public static class ServiceType + { + public const string Master = "Master"; + public const string Server = "Server"; + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/direct/CustomTypeExtensions.cs b/Microsoft.Azure.Cosmos/src/direct/CustomTypeExtensions.cs index 66f9cc89c5..26b4c957db 100644 --- a/Microsoft.Azure.Cosmos/src/direct/CustomTypeExtensions.cs +++ b/Microsoft.Azure.Cosmos/src/direct/CustomTypeExtensions.cs @@ -35,7 +35,7 @@ internal static class CustomTypeExtensions #if COSMOSCLIENT public const string SDKName = "cosmos-netstandard-sdk"; - public const string SDKVersion = "3.39.1"; + public const string SDKVersion = "3.18.0"; #else public const string SDKName = "documentdb-netcore-sdk"; public const string SDKVersion = "2.14.0"; diff --git a/Microsoft.Azure.Cosmos/src/direct/DataMaskingPolicy.cs b/Microsoft.Azure.Cosmos/src/direct/DataMaskingPolicy.cs index 6bf5a78caf..bb1bb16a30 100644 --- a/Microsoft.Azure.Cosmos/src/direct/DataMaskingPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/direct/DataMaskingPolicy.cs @@ -16,7 +16,6 @@ namespace Microsoft.Azure.Documents internal sealed class DataMaskingPolicy : JsonSerializable { private Collection includedPaths; - private Boolean isPolicyEnabled; /// /// Initializes a new instance of the class for the Azure Cosmos DB service. @@ -56,45 +55,11 @@ public Collection IncludedPaths } } - /// - /// Paths of the item that need masked along with path-specific settings. - /// - [JsonProperty(PropertyName = Constants.Properties.IsPolicyEnabled)] - public Boolean IsPolicyEnabled - { - get - { -#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' -#pragma warning disable IDE0074 // Use compound assignment - if (this.isPolicyEnabled == null) - { - this.isPolicyEnabled = base.GetValue(Constants.Properties.IsPolicyEnabled, true); - } -#pragma warning restore IDE0074 // Use compound assignment -#pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' - - return this.isPolicyEnabled; - } - set - { -#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' - if (value == null) - { - throw new ArgumentNullException(string.Format(CultureInfo.CurrentCulture, RMResources.PropertyCannotBeNull, nameof(IsPolicyEnabled))); - } -#pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' - - this.isPolicyEnabled = value; - base.SetValue(Constants.Properties.IsPolicyEnabled, this.IsPolicyEnabled); - } - } - internal override void OnSave() { if (this.includedPaths != null) { base.SetObjectCollection(Constants.Properties.IncludedPaths, this.includedPaths); - base.SetValue(Constants.Properties.IsPolicyEnabled, this.isPolicyEnabled); } } } diff --git a/Microsoft.Azure.Cosmos/src/direct/DictionaryNameValueCollection.cs b/Microsoft.Azure.Cosmos/src/direct/DictionaryNameValueCollection.cs index 547cc08510..a594d2fc4f 100644 --- a/Microsoft.Azure.Cosmos/src/direct/DictionaryNameValueCollection.cs +++ b/Microsoft.Azure.Cosmos/src/direct/DictionaryNameValueCollection.cs @@ -11,9 +11,7 @@ namespace Microsoft.Azure.Documents.Collections internal sealed class DictionaryNameValueCollection: INameValueCollection { -#pragma warning disable IDE0044 // Add readonly modifier private static StringComparer defaultStringComparer = StringComparer.OrdinalIgnoreCase; -#pragma warning restore IDE0044 // Add readonly modifier private readonly Dictionary dictionary; private CompositeValue nullValue; @@ -323,9 +321,7 @@ public NameValueCollection ToNameValueCollection() /// private class CompositeValue { -#pragma warning disable IDE0044 // Add readonly modifier private List values; -#pragma warning restore IDE0044 // Add readonly modifier internal CompositeValue() { diff --git a/Microsoft.Azure.Cosmos/src/direct/Dispatcher.cs b/Microsoft.Azure.Cosmos/src/direct/Dispatcher.cs index 24b020badd..06a776138a 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Dispatcher.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Dispatcher.cs @@ -12,6 +12,7 @@ namespace Microsoft.Azure.Documents.Rntbd using System.Net.Security; using System.Threading; using System.Threading.Tasks; + using System.Security.Cryptography.X509Certificates; using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Documents.FaultInjection; #if COSMOSCLIENT @@ -40,6 +41,7 @@ internal sealed class Dispatcher : IDisposable private readonly CancellationTokenSource cancellation = new CancellationTokenSource(); private readonly TimerPool idleTimerPool; private readonly bool enableChannelMultiplexing; + private readonly Action clientCertificateFailureHandler; private bool disposed = false; @@ -52,6 +54,7 @@ internal sealed class Dispatcher : IDisposable private Task receiveTask = null; // Guarded by callLock. // Guarded by callLock. private readonly Dictionary calls = new Dictionary(); + private readonly bool shouldRequestMutualTls = false; // Guarded by callLock. // The call map can become frozen if the underlying connection becomes @@ -83,6 +86,8 @@ public Dispatcher( bool enableChannelMultiplexing, MemoryStreamPool memoryStreamPool, RemoteCertificateValidationCallback remoteCertificateValidationCallback, + Func clientCertificateFunction, + Action clientCertificateFailureHandler, Func> dnsResolutionFunction, IChaosInterceptor chaosInterceptor) { @@ -92,6 +97,7 @@ public Dispatcher( idleTimeout, memoryStreamPool, remoteCertificateValidationCallback, + clientCertificateFunction, dnsResolutionFunction); this.userAgent = userAgent; this.connectionStateListener = connectionStateListener; @@ -99,6 +105,8 @@ public Dispatcher( this.idleTimerPool = idleTimerPool; this.enableChannelMultiplexing = enableChannelMultiplexing; this.chaosInterceptor = chaosInterceptor; + this.clientCertificateFailureHandler = clientCertificateFailureHandler; + this.shouldRequestMutualTls = clientCertificateFunction != null; } internal Dispatcher( @@ -118,13 +126,11 @@ internal Dispatcher( this.enableChannelMultiplexing = enableChannelMultiplexing; this.chaosInterceptor = chaosInterceptor; } -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row #region Test hook. internal event Action TestOnConnectionClosed; -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row internal bool TestIsIdle { get @@ -353,7 +359,6 @@ public async Task CallAsync(ChannelCallArguments args, TransportR try { try -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row { if (this.chaosInterceptor != null) { @@ -379,7 +384,6 @@ await this.connection.WriteRequestAsync( } -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row catch (Exception e) { callInfo.SendFailed(); @@ -577,7 +581,6 @@ private void ScheduleIdleTimer(TimeSpan timeToIdle) Debug.Assert(Monitor.IsEntered(this.connectionLock)); this.idleTimer = this.idleTimerPool.GetPooledTimer((int)timeToIdle.TotalSeconds); this.idleTimerTask = this.idleTimer.StartTimerAsync().ContinueWith(this.OnIdleTimer, TaskContinuationOptions.OnlyOnRanToCompletion); -#pragma warning disable SA1137 this.idleTimerTask.ContinueWith( failedTask => { @@ -586,7 +589,6 @@ private void ScheduleIdleTimer(TimeSpan timeToIdle) this.ConnectionCorrelationId, this, failedTask.Exception?.InnerException?.Message); }, TaskContinuationOptions.OnlyOnFaulted); -#pragma warning restore SA1137 } // this.connectionLock must be held. @@ -692,7 +694,11 @@ private void ThrowIfDisposed() private async Task NegotiateRntbdContextAsync(ChannelOpenArguments args) { byte[] contextMessage = TransportSerialization.BuildContextRequest( - args.CommonArguments.ActivityId, this.userAgent, args.CallerId, this.enableChannelMultiplexing); + args.CommonArguments.ActivityId, + this.userAgent, + args.CallerId, + this.enableChannelMultiplexing, + mutualTlsAuthMode: this.shouldRequestMutualTls ? RntbdConstants.RntbdMutualTlsAuthMode.System : null); await this.connection.WriteRequestAsync( args.CommonArguments, @@ -732,6 +738,7 @@ await this.connection.WriteRequestAsync( { Error error = Resource.LoadFrom(errorResponseStream); + // DEVNOTE: This exception can be sent to the user via the clientCertificateFailureHandler callback. DocumentClientException exception = new DocumentClientException( string.Format(CultureInfo.CurrentUICulture, RMResources.ExceptionMessage, @@ -765,10 +772,39 @@ await this.connection.WriteRequestAsync( BytesSerializer.GetStringFromBytes(response.serverVersion.value.valueBytes)); } + bool isMutualTlsAuthFailure = false; + if (response.mutualTlsAuthThumbprint.isPresent) + { + exception.Headers.Add(HttpConstants.HttpHeaders.MutualTlsThumbprint, + BytesSerializer.GetStringFromBytes(response.mutualTlsAuthThumbprint.value.valueBytes)); + exception.Headers.Add(HttpConstants.HttpHeaders.MutualTlsStatus, + HttpConstants.HttpHeaderValues.MutualTlsAuthFailed); + isMutualTlsAuthFailure = true; + } + + // DEVNOTE: Do not modify the exception after this point, as it may affect the mutual TLS auth failure handler. + if (isMutualTlsAuthFailure) + { + this.clientCertificateFailureHandler?.Invoke( + exception.Headers.Get(HttpConstants.HttpHeaders.MutualTlsThumbprint), + exception); + } + throw exception; } } + // Even on successful requests, we want to handle Mutual TLS auth failures. + // This is because while we are in dual mode (certificate + token as fallback), + // the server may indicate that the certificate auth failed, but requests can still succeed with the auth token. + // In the future, once Mutual TLS auth is the only auth method, this can be removed as a cert failure will be a 401. + if (response.mutualTlsAuthThumbprint.isPresent) + { + this.clientCertificateFailureHandler?.Invoke( + BytesSerializer.GetStringFromBytes(response.mutualTlsAuthThumbprint.value.valueBytes), + null); + } + this.NotifyConnectionOnSuccessEvent(); args.OpenTimeline.RecordRntbdHandshakeFinishTime(); } diff --git a/Microsoft.Azure.Cosmos/src/direct/DocumentCollection.cs b/Microsoft.Azure.Cosmos/src/direct/DocumentCollection.cs index 92f0a89fad..feaef6ef54 100644 --- a/Microsoft.Azure.Cosmos/src/direct/DocumentCollection.cs +++ b/Microsoft.Azure.Cosmos/src/direct/DocumentCollection.cs @@ -110,7 +110,10 @@ class DocumentCollection : Resource private ConflictResolutionPolicy conflictResolutionPolicy; private ChangeFeedPolicy changeFeedPolicy; private CollectionBackupPolicy collectionBackupPolicy; + private ExternalBackupFeatureFlags externalBackupFeatureFlags; + private PreviousImageRetentionPolicy previousImageRetentionPolicy; private MaterializedViewDefinition materializedViewDefinition; + private MaterializedViewsProperties materializedViewsProperties; private ByokConfig byokConfig; private ClientEncryptionPolicy clientEncryptionPolicy; private DataMaskingPolicy dataMaskingPolicy; @@ -286,6 +289,28 @@ internal MaterializedViewDefinition MaterializedViewDefinition } } + /// + /// Gets the associated with the collection. + /// + [JsonProperty(PropertyName = Constants.Properties.MaterializedViewsProperties)] + internal MaterializedViewsProperties MaterializedViewsProperties + { + get + { + if (this.materializedViewsProperties == null) + { + this.materializedViewsProperties = base.GetObject(Constants.Properties.MaterializedViewsProperties) ?? null; + } + + return this.materializedViewsProperties; + } + set + { + this.materializedViewsProperties = value; + base.SetObject(Constants.Properties.MaterializedViewsProperties, value); + } + } + /// /// Gets the associated with the collection from the Azure Cosmos DB service. /// @@ -326,12 +351,6 @@ internal byte UniqueIndexNameEncodingMode } set { -#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' - if(value == null) - { - throw new ArgumentNullException(string.Format(CultureInfo.CurrentCulture, RMResources.PropertyCannotBeNull, "UniqueIndexNameEncodingMode")); - } -#pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' this.uniqueIndexNameEncodingMode = value; this.SetValue(Constants.Properties.UniqueIndexNameEncodingMode, value); } @@ -883,6 +902,36 @@ internal CollectionBackupPolicy CollectionBackupPolicy } } + /// + /// Gets the associated with the collection from the Azure Cosmos DB service. + /// + /// + /// The external backup feature flags associated with the collection. + /// + [JsonProperty(PropertyName = Constants.Properties.ExternalBackupFeatureFlags, DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore)] + internal ExternalBackupFeatureFlags ExternalBackupFeatureFlags + { + get + { + if (this.externalBackupFeatureFlags == null) + { + this.externalBackupFeatureFlags = base.GetObject(Constants.Properties.ExternalBackupFeatureFlags); + } + + return this.externalBackupFeatureFlags; + } + set + { + if (value == null) + { + throw new ArgumentNullException(string.Format(CultureInfo.CurrentCulture, RMResources.PropertyCannotBeNull, "ExternalBackupFeatureFlags")); + } + + this.externalBackupFeatureFlags = value; + base.SetObject(Constants.Properties.ExternalBackupFeatureFlags, value); + } + } + /// /// Gets the associated with the collection from the Azure Cosmos DB service. /// @@ -906,7 +955,7 @@ internal CollectionTieringPolicy CollectionTieringPolicy if (value == null) { throw new ArgumentNullException(string.Format(CultureInfo.CurrentCulture, RMResources.PropertyCannotBeNull, "CollectionTieringPolicy")); - } + } this.collectionTieringPolicy = value; base.SetObject(Constants.Properties.CollectionTieringPolicy, value); @@ -951,7 +1000,7 @@ internal InternalSchemaProperties InternalSchemaProperties set { base.SetValue(Constants.Properties.InternalSchemaProperties, value); - } + } } internal bool IsMaterializedView() @@ -990,16 +1039,13 @@ internal ClientEncryptionPolicy ClientEncryptionPolicy } } - // - -#pragma warning disable CS1570 // XML comment has badly formed XML -/// Gets the associated with the collection from the Azure Cosmos DB service. + /// + /// Gets the associated with the collection from the Azure Cosmos DB service. /// /// /// The DataMaskingPolicy associated with the collection. /// [JsonProperty(PropertyName = Constants.Properties.DataMaskingPolicy, DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore)] -#pragma warning restore CS1570 // XML comment has badly formed XML internal DataMaskingPolicy DataMaskingPolicy { get @@ -1023,16 +1069,13 @@ internal DataMaskingPolicy DataMaskingPolicy } } - // - -#pragma warning disable CS1570 // XML comment has badly formed XML -/// Gets the associated with the collection from the Azure Cosmos DB service. + /// + /// Gets the associated with the collection from the Azure Cosmos DB service. /// /// /// The VectorEmbeddingPolicy associated with the collection. /// [JsonProperty(PropertyName = Constants.Properties.VectorEmbeddingPolicy, DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore)] -#pragma warning restore CS1570 // XML comment has badly formed XML internal VectorEmbeddingPolicy VectorEmbeddingPolicy { get @@ -1056,16 +1099,13 @@ internal VectorEmbeddingPolicy VectorEmbeddingPolicy } } - // - -#pragma warning disable CS1570 // XML comment has badly formed XML -/// Gets the associated with the collection from the Azure Cosmos DB service. + /// + /// Gets the associated with the collection from the Azure Cosmos DB service. /// /// /// The FullTextPolicy associated with the collection. /// [JsonProperty(PropertyName = Constants.Properties.FullTextPolicy, DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore)] -#pragma warning restore CS1570 // XML comment has badly formed XML internal FullTextPolicy FullTextPolicy { get @@ -1089,16 +1129,13 @@ internal FullTextPolicy FullTextPolicy } } - // - -#pragma warning disable CS1570 // XML comment has badly formed XML -/// Gets the associated with the collection from the Azure Cosmos DB service. + /// + /// Gets the associated with the collection from the Azure Cosmos DB service. /// /// /// The FullTextPolicy associated with the collection. /// [JsonProperty(PropertyName = Constants.SoftDeletionMetadataProperties.SoftDeletionMetadata, DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore)] -#pragma warning restore CS1570 // XML comment has badly formed XML internal SoftDeletionMetadata SoftDeletionMetadata { get @@ -1117,6 +1154,31 @@ internal SoftDeletionMetadata SoftDeletionMetadata } } + /// + /// Gets or sets the associated with the collection from the Azure Cosmos DB service. + /// + /// + /// The previous image retention policy associated with the collection. + /// + [JsonProperty(PropertyName = Constants.Properties.PreviousImageRetentionPolicy, DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore)] + internal PreviousImageRetentionPolicy PreviousImageRetentionPolicy + { + get + { + if (this.previousImageRetentionPolicy == null) + { + this.previousImageRetentionPolicy = base.GetObject(Constants.Properties.PreviousImageRetentionPolicy); + } + + return this.previousImageRetentionPolicy; + } + set + { + this.previousImageRetentionPolicy = value; + base.SetObject(Constants.Properties.PreviousImageRetentionPolicy, value); + } + } + /// /// Gets the materialized views on the collection. /// @@ -1268,7 +1330,7 @@ internal override void OnSave() { base.SetObject(Constants.Properties.SchemaDiscoveryPolicy, this.schemaDiscoveryPolicy); } - + if (this.changeFeedPolicy != null) { base.SetObject(Constants.Properties.ChangeFeedPolicy, this.changeFeedPolicy); @@ -1279,6 +1341,16 @@ internal override void OnSave() base.SetObject(Constants.Properties.CollectionBackupPolicy, this.collectionBackupPolicy); } + if (this.externalBackupFeatureFlags != null) + { + base.SetObject(Constants.Properties.ExternalBackupFeatureFlags, this.externalBackupFeatureFlags); + } + + if (this.previousImageRetentionPolicy != null) + { + base.SetObject(Constants.Properties.PreviousImageRetentionPolicy, this.previousImageRetentionPolicy); + } + if (this.collectionTieringPolicy != null) { base.SetObject(Constants.Properties.CollectionTieringPolicy, this.collectionTieringPolicy); @@ -1289,7 +1361,7 @@ internal override void OnSave() base.SetObject(Constants.Properties.GeospatialConfig, this.geospatialConfig); } - if(this.byokConfig != null) + if (this.byokConfig != null) { base.SetObject(Constants.Properties.ByokConfig, this.byokConfig); } @@ -1332,8 +1404,8 @@ internal override void OnSave() base.SetObject(Constants.EncryptionScopeProperties.EncryptionScope, this.encryptionScopeMetadata); } - if(this.uniqueIndexNameEncodingMode != 0) - { + if (this.uniqueIndexNameEncodingMode != 0) + { base.SetValue(Constants.Properties.UniqueIndexNameEncodingMode, this.uniqueIndexNameEncodingMode); } diff --git a/Microsoft.Azure.Cosmos/src/direct/DocumentResponse.cs b/Microsoft.Azure.Cosmos/src/direct/DocumentResponse.cs index 562bf41505..86081cca9e 100644 --- a/Microsoft.Azure.Cosmos/src/direct/DocumentResponse.cs +++ b/Microsoft.Azure.Cosmos/src/direct/DocumentResponse.cs @@ -35,9 +35,7 @@ namespace Microsoft.Azure.Documents.Client sealed class DocumentResponse : ResourceResponseBase, IDocumentResponse { private TDocument document; -#pragma warning disable IDE0044 // Add readonly modifier private JsonSerializerSettings settings; -#pragma warning restore IDE0044 // Add readonly modifier /// /// Constructor exposed for mocking purposes for the Azure Cosmos DB service. diff --git a/Microsoft.Azure.Cosmos/src/direct/DocumentServiceRequest.cs b/Microsoft.Azure.Cosmos/src/direct/DocumentServiceRequest.cs index 5d321d64ee..72947719ab 100644 --- a/Microsoft.Azure.Cosmos/src/direct/DocumentServiceRequest.cs +++ b/Microsoft.Azure.Cosmos/src/direct/DocumentServiceRequest.cs @@ -389,7 +389,7 @@ public bool IsBodySeekableClonableAndCountable } } - public OperationType OperationType { get; private set; } + public OperationType OperationType { get; set; } public ResourceType ResourceType { get; private set; } @@ -635,6 +635,7 @@ public string HttpMethod case OperationType.GetUnwrappedDek: case OperationType.GetDekProperties: case OperationType.GetFederationConfigurations: + case OperationType.GetRegionalConfigurations: case OperationType.GetDatabaseAccountConfigurations: case OperationType.GetStorageServiceConfigurations: case OperationType.GetDatabaseAccountArtifactPermissions: @@ -650,6 +651,11 @@ public string HttpMethod case OperationType.XPDatabaseAccountMetaData: case OperationType.Truncate: case OperationType.RelocateLeakedTentativeWrites: + case OperationType.ExternalBackup: + case OperationType.ExternalBackupRestore: + case OperationType.CancelExternalBackupRestore: + case OperationType.GetAzureRbacAccessCheck: + case OperationType.CancelExternalBackup: return HttpConstants.HttpMethods.Post; case OperationType.EnsureSnapshotOperation: @@ -660,6 +666,9 @@ public string HttpMethod case OperationType.GetStorageAuthToken: case OperationType.GetCustomerManagedKeyStatus: case OperationType.GetGraphDatabaseAccountConfiguration: + case OperationType.ExternalPreBackup: + case OperationType.CheckExternalBackupStatus: + case OperationType.CheckExternalBackupRestoreStatus: return HttpConstants.HttpMethods.Get; #endif @@ -791,6 +800,10 @@ public bool IsValidAddress(ResourceType resourceType = ResourceType.Unknown) { return true; } + else if (this.ResourceType == ResourceType.AzureRbac) + { + return true; + } #if !COSMOSCLIENT else if (this.ResourceType == ResourceType.VectorClock) { @@ -840,15 +853,26 @@ public void AddPreferHeader(string preferHeaderName, string preferHeaderValue) public static DocumentServiceRequest CreateFromResource( DocumentServiceRequest request, Resource modifiedResource) + { + return CreateFromResourceWithModifiedOperationType( + request, + modifiedResource, + request.OperationType); + } + + public static DocumentServiceRequest CreateFromResourceWithModifiedOperationType( + DocumentServiceRequest request, + Resource modifiedResource, + OperationType operationType) { DocumentServiceRequest modifiedRequest; if (!request.IsNameBased) { - modifiedRequest = DocumentServiceRequest.Create(request.OperationType, modifiedResource, request.ResourceType, request.RequestAuthorizationTokenType, request.Headers, request.ResourceId); + modifiedRequest = DocumentServiceRequest.Create(operationType, modifiedResource, request.ResourceType, request.RequestAuthorizationTokenType, request.Headers, request.ResourceId); } else { - modifiedRequest = DocumentServiceRequest.CreateFromName(request.OperationType, modifiedResource, request.ResourceType, request.Headers, request.ResourceAddress, request.RequestAuthorizationTokenType); + modifiedRequest = DocumentServiceRequest.CreateFromName(operationType, modifiedResource, request.ResourceType, request.Headers, request.ResourceAddress, request.RequestAuthorizationTokenType); } return modifiedRequest; @@ -1156,7 +1180,7 @@ public void ClearRoutingHints() this.RequestContext.ResolvedPartitionKeyRange = null; } - public DocumentServiceRequest Clone() + public DocumentServiceRequest Clone(bool ignoreCloneableBody = false) { if (!this.IsBodySeekableClonableAndCountable) { @@ -1170,7 +1194,7 @@ public DocumentServiceRequest Clone() ServiceIdentity = this.ServiceIdentity, SystemAuthorizationParams = this.SystemAuthorizationParams == null ? null : this.SystemAuthorizationParams.Clone(), // Body = this.Body, // intentionally don't clone body, as it is not cloneable. - CloneableBody = this.CloneableBody != null ? this.CloneableBody.Clone() : null, + CloneableBody = !ignoreCloneableBody && this.CloneableBody != null ? this.CloneableBody.Clone() : null, Headers = (INameValueCollection)this.Headers.Clone(), IsFeed = this.IsFeed, IsNameBased = this.IsNameBased, @@ -1241,7 +1265,8 @@ private void InitializeWithDataParsedFromUri( !(this.ResourceType == ResourceType.RoleDefinition) && !(this.ResourceType == ResourceType.RoleAssignment) && !(this.ResourceType == ResourceType.InteropUser) && - !(this.ResourceType == ResourceType.AuthPolicyElement) + !(this.ResourceType == ResourceType.AuthPolicyElement) && + !(this.ResourceType == ResourceType.AzureRbac) #if !COSMOSCLIENT && !(this.ResourceType == ResourceType.MasterPartition) && !(this.ResourceType == ResourceType.ServerPartition) && diff --git a/Microsoft.Azure.Cosmos/src/direct/DocumentServiceRequestContext.cs b/Microsoft.Azure.Cosmos/src/direct/DocumentServiceRequestContext.cs index c18fa00f85..22eaedf552 100644 --- a/Microsoft.Azure.Cosmos/src/direct/DocumentServiceRequestContext.cs +++ b/Microsoft.Azure.Cosmos/src/direct/DocumentServiceRequestContext.cs @@ -39,10 +39,18 @@ internal sealed class DocumentServiceRequestContext public long GlobalCommittedSelectedLSN { get; set; } /// - /// Cache the write storeResult in context during global strong - /// where we want to lock on a single initial write response and perform barrier calls until globalCommittedLsn is caught up + /// For strong consistency writes, this tracks the endpoint of the region the write was sent to. + /// This is used to ensure that on retries, the barrier requests are sent to the same region. + /// If a failover occurs and the region endpoint changes, the request is failed. /// - public ReferenceCountedDisposable GlobalStrongWriteStoreResult { get; set; } + public Uri GlobalStrongWriteEndpoint { get; set; } + + /// + /// Cache the write storeResult in context during global strong or less than strong consistency writes + /// where we want to lock on a single initial write response and perform barrier calls until globalCommittedLsn + /// or the globalNRegion CommittedLsn is caught up. + /// + public ReferenceCountedDisposable CachedWriteStoreResult { get; set; } /// /// Unique Identity that represents the target partition where the request should reach. @@ -108,9 +116,12 @@ public IClientSideRequestStatistics ClientRequestStatistics public bool IsRetry { get; set; } /// - /// Indicates if this request is being retried on a different region due to Per partition failover. + /// Indicates if this request wants to explictly opt out of Per partition failover retry behavior. + /// Ex: Topology upsert with PrepareForEntityDeletionIntent currently needs to skip partition failover retry. + /// Once backup restore is able to fully support collection and database deletes, this can be removed. + /// Ex: If a request is being retried on a different region due to Per partition failover, then skip. /// - public bool IsPartitionFailoverRetry { get; set; } + public bool SkipPartitionFailoverRetry { get; set; } /// /// A list of regions to exclude routing to, used for per-request level routing exclusion @@ -162,31 +173,25 @@ public void AddToFailedEndpoints(Exception storeException, TransportAddressUri t } } - -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -/// + /// /// Sets routing directive for to resolve /// the request to endpoint based on location index /// /// Index of the location to which the request should be routed /// Use preferred locations to route request public void RouteToLocation(int locationIndex, bool usePreferredLocations) -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved { this.LocationIndexToRoute = locationIndex; this.UsePreferredLocations = usePreferredLocations; this.LocationEndpointToRoute = null; } - -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -/// + /// /// Sets location-based routing directive for to resolve /// the request to given /// /// Location endpoint to which the request should be routed public void RouteToLocation(Uri locationEndpoint) -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved { this.LocationEndpointToRoute = locationEndpoint; this.LocationIndexToRoute = null; @@ -249,6 +254,7 @@ public DocumentServiceRequestContext Clone() requestContext.FailedEndpoints = this.FailedEndpoints; requestContext.LastPartitionAddressInformationHashCode = this.LastPartitionAddressInformationHashCode; requestContext.ExcludeRegions = this.ExcludeRegions; + requestContext.GlobalStrongWriteEndpoint = this.GlobalStrongWriteEndpoint; return requestContext; } diff --git a/Microsoft.Azure.Cosmos/src/direct/Embedding.cs b/Microsoft.Azure.Cosmos/src/direct/Embedding.cs index 03cad849cd..f755decf56 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Embedding.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Embedding.cs @@ -5,6 +5,7 @@ namespace Microsoft.Azure.Documents { using System; + using System.Globalization; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -13,6 +14,8 @@ namespace Microsoft.Azure.Documents /// internal sealed class Embedding : JsonSerializable { + private EmbeddingSource embeddingSource; + public Embedding() { } @@ -92,5 +95,30 @@ public DistanceFunction DistanceFunction base.SetValue(Constants.Properties.DistanceFunction, value); } } + + /// + /// Gets the EmbeddingSource associated with the Embedding. + /// + /// + /// The EmbeddingSource associated with the VectorEmbeddingPolicy. + /// + [JsonProperty(PropertyName = Constants.Properties.EmbeddingSource, DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore)] + public EmbeddingSource EmbeddingSource + { + get + { + if (this.embeddingSource == null) + { + this.embeddingSource = base.GetObject(Constants.Properties.EmbeddingSource); + } + + return this.embeddingSource; + } + set + { + this.embeddingSource = value; + base.SetObject(Constants.Properties.EmbeddingSource, value); + } + } } } diff --git a/Microsoft.Azure.Cosmos/src/direct/EmbeddingSource.cs b/Microsoft.Azure.Cosmos/src/direct/EmbeddingSource.cs new file mode 100644 index 0000000000..e035543e69 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/direct/EmbeddingSource.cs @@ -0,0 +1,166 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Documents +{ + using System; + using System.Collections.Generic; + using System.Runtime.Serialization; + using Newtonsoft.Json; + using Newtonsoft.Json.Converters; + + /// + /// Represents the embedding source settings for the vector embedding policy. + /// + internal sealed class EmbeddingSource : JsonSerializable + { + public EmbeddingSource() + { + } + + /// + /// Gets or sets the source path of the embedding source. + /// + [JsonProperty(PropertyName = Constants.Properties.SourcePath)] + public string SourcePath + { + get + { + return base.GetValue(Constants.Properties.SourcePath); + } + set + { + base.SetValue(Constants.Properties.SourcePath, value); + } + } + + /// + /// Gets or sets the auth type of the embedding source. + /// + [JsonProperty(PropertyName = Constants.Properties.AuthType)] + public AuthenticationType AuthType + { + get + { + AuthenticationType result = default; + string strValue = base.GetValue(Constants.Properties.AuthType); + if (!string.IsNullOrEmpty(strValue)) + { + Enum.TryParse(strValue, true, out result); + } + return result; + } + set + { + base.SetValue(Constants.Properties.AuthType, value); + } + } + + /// + /// Gets or sets the deployment name of the embedding source. + /// + [JsonProperty(PropertyName = Constants.Properties.DeploymentName)] + public string DeploymentName + { + get + { + return base.GetValue(Constants.Properties.DeploymentName); + } + set + { + base.SetValue(Constants.Properties.DeploymentName, value); + } + } + + /// + /// Gets or sets the endpoint of the embedding source. + /// + [JsonProperty(PropertyName = Constants.Properties.Endpoint)] + public string Endpoint + { + get + { + return base.GetValue(Constants.Properties.Endpoint); + } + set + { + base.SetValue(Constants.Properties.Endpoint, value); + } + } + + /// + /// Gets or sets the model name of the embedding source. + /// + [JsonProperty(PropertyName = Constants.Properties.ModelName)] + public string ModelName + { + get + { + return base.GetValue(Constants.Properties.ModelName); + } + set + { + base.SetValue(Constants.Properties.ModelName, value); + } + } + + /// + public override bool Equals(object obj) + { + if (!(obj is EmbeddingSource other)) + { + return false; + } + + return this.SourcePath == other.SourcePath && + this.AuthType == other.AuthType && + this.DeploymentName == other.DeploymentName && + this.Endpoint == other.Endpoint && + this.ModelName == other.ModelName; + } + + /// + public override int GetHashCode() + { + int hashCode = 1265339359; + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.SourcePath); + hashCode = (hashCode * -1521134295) + this.AuthType.GetHashCode(); + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.DeploymentName); + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.Endpoint); + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.ModelName); + return hashCode; + } + + internal override void Validate() + { + base.Validate(); + Helpers.ValidateEnumProperties(this.AuthType); + } + + /// + /// Defines the type of Authentication while calling embedding provider api in the Azure Cosmos DB's Embedding Generator service. + /// + [JsonConverter(typeof(StringEnumConverter))] + public enum AuthenticationType : short + { + /// + /// Represent unknown authentication. + /// + [EnumMember(Value = "Unknown")] + Unknown, + + /// + /// Represent Entra authentication type. + /// + [EnumMember(Value = "Entra")] + Entra, + + /// + /// Represent Key based authentication type. + /// + [EnumMember(Value = "ApiKey")] + ApiKey, + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/direct/ExternalBackupFeatureFlags.cs b/Microsoft.Azure.Cosmos/src/direct/ExternalBackupFeatureFlags.cs new file mode 100644 index 0000000000..cf1af85657 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/direct/ExternalBackupFeatureFlags.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Documents +{ + using System; + using Newtonsoft.Json; + + internal sealed class ExternalBackupFeatureFlags : JsonSerializable, ICloneable + { + /// + /// Initializes a new instance of the class. + /// + public ExternalBackupFeatureFlags() + { + this.AzureBackupVaultEnabled = false; + } + + [JsonProperty(PropertyName = Constants.Properties.AzureBackupVaultEnabled)] + public bool AzureBackupVaultEnabled + { + get + { + return base.GetValue(Constants.Properties.AzureBackupVaultEnabled); + } + set + { + base.SetValue(Constants.Properties.AzureBackupVaultEnabled, value); + } + } + + public object Clone() + { + return new ExternalBackupFeatureFlags() + { + AzureBackupVaultEnabled = this.AzureBackupVaultEnabled, + }; + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/direct/FeedResource.cs b/Microsoft.Azure.Cosmos/src/direct/FeedResource.cs index 750bbbc884..ac79d33eb8 100644 --- a/Microsoft.Azure.Cosmos/src/direct/FeedResource.cs +++ b/Microsoft.Azure.Cosmos/src/direct/FeedResource.cs @@ -26,6 +26,10 @@ private static string CollectionName { FeedResource.collectionName = "Attachments"; } + else if (typeof(AzureRbac).IsAssignableFrom(typeof(T))) + { + FeedResource.collectionName = "AzureRbac"; + } else { FeedResource.collectionName = typeof(T).Name + "s"; diff --git a/Microsoft.Azure.Cosmos/src/direct/FullTextPolicy.cs b/Microsoft.Azure.Cosmos/src/direct/FullTextPolicy.cs index 0b9aa43ac0..cb2ef9cd04 100644 --- a/Microsoft.Azure.Cosmos/src/direct/FullTextPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/direct/FullTextPolicy.cs @@ -72,7 +72,7 @@ public Collection FullTextPaths internal override void OnSave() { - this.SetValue(Constants.Properties.Path, this.DefaultLanguage); + this.SetValue(Constants.Properties.DefaultLanguage, this.DefaultLanguage); if (this.fullTextPaths != null) { diff --git a/Microsoft.Azure.Cosmos/src/direct/GoneAndRetryWithRequestRetryPolicy.cs b/Microsoft.Azure.Cosmos/src/direct/GoneAndRetryWithRequestRetryPolicy.cs index 541bba0a01..ac49ea2fdb 100644 --- a/Microsoft.Azure.Cosmos/src/direct/GoneAndRetryWithRequestRetryPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/direct/GoneAndRetryWithRequestRetryPolicy.cs @@ -21,10 +21,8 @@ internal sealed class GoneAndRetryWithRequestRetryPolicy : { struct ErrorOrResponse { -#pragma warning disable IDE0044 // Add readonly modifier private Exception exception; private int statusCode; -#pragma warning restore IDE0044 // Add readonly modifier public ErrorOrResponse(Exception ex, TResponse response) { @@ -69,9 +67,7 @@ private string ExceptionToString() private readonly int? randomSaltForRetryWithMilliseconds; -#pragma warning disable IDE0044 // Add readonly modifier private Stopwatch durationTimer = new Stopwatch(); -#pragma warning restore IDE0044 // Add readonly modifier private TimeSpan minBackoffForRegionReroute; private int attemptCount = 1; private int attemptCountInvalidPartition = 1; @@ -396,6 +392,14 @@ public bool TryHandleResponseSynchronously(DocumentServiceRequest request, TResp } else if (GoneAndRetryWithRequestRetryPolicy.IsPartitionKeySplitting(response, exception)) { + DefaultTrace.TraceInformation( + "GoneAndRetryWithRequestRetryPolicy handling PartitionKeyRangeIsSplitting exception. CollectionName: {0}, OperationType: {1}, ResourceType: {2}, ResourceId: {3}, Address: {4}, Exception: {5}", + request.CollectionName, + request.OperationType, + request.ResourceType, + request.ResourceId, + request.ResourceAddress, + new ErrorOrResponse(exception, response)); GoneAndRetryWithRequestRetryPolicy.ClearRequestContext(request); request.ForcePartitionKeyRangeRefresh = true; forceRefreshAddressCache = false; diff --git a/Microsoft.Azure.Cosmos/src/direct/GoneAndRetryWithRetryPolicy.cs b/Microsoft.Azure.Cosmos/src/direct/GoneAndRetryWithRetryPolicy.cs index fb780cb68f..6bf57477b2 100644 --- a/Microsoft.Azure.Cosmos/src/direct/GoneAndRetryWithRetryPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/direct/GoneAndRetryWithRetryPolicy.cs @@ -28,9 +28,7 @@ internal sealed class GoneAndRetryWithRetryPolicy : private const int minFailedReplicaCountToConsiderConnectivityIssue = 3; -#pragma warning disable IDE0044 // Add readonly modifier private Stopwatch durationTimer = new Stopwatch(); -#pragma warning restore IDE0044 // Add readonly modifier private int attemptCount = 1; private int attemptCountInvalidPartition = 1; private int regionRerouteAttemptCount = 0; @@ -46,9 +44,7 @@ internal sealed class GoneAndRetryWithRetryPolicy : // Don't penalise first retry with delay. private int currentBackoffSeconds = GoneAndRetryWithRetryPolicy.initialBackoffSeconds; -#pragma warning disable IDE0044 // Add readonly modifier private DocumentServiceRequest request; -#pragma warning restore IDE0044 // Add readonly modifier public GoneAndRetryWithRetryPolicy( DocumentServiceRequest request = null, @@ -193,7 +189,6 @@ exception is PartitionKeyRangeGoneException || { DefaultTrace.TraceError("{0}. Will fail the request. {1}", message, exception.ToStringWithData()); SubStatusCodes exceptionSubStatus = DocumentClientException.GetExceptionSubStatusForGoneRetryPolicy(exception); -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row if (this.detectConnectivityIssues && @@ -208,7 +203,6 @@ exception is PartitionKeyRangeGoneException || 1 : this.request.RequestContext.ClientRequestStatistics.RegionsContacted.Count), SubStatusCodes.Client_CPUOverload); } -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row if (this.detectConnectivityIssues && this.request.RequestContext.ClientRequestStatistics != null && this.request.RequestContext.ClientRequestStatistics.IsCpuThreadStarvation.GetValueOrDefault(false)) @@ -224,7 +218,6 @@ exception is PartitionKeyRangeGoneException || else if (this.detectConnectivityIssues && this.request.RequestContext.ClientRequestStatistics != null && this.request.RequestContext.ClientRequestStatistics.FailedReplicas.Count >= GoneAndRetryWithRetryPolicy.minFailedReplicaCountToConsiderConnectivityIssue) -#pragma warning disable SA1505 // Opening braces should not be followed by blank line { exceptionToThrow = new ServiceUnavailableException( @@ -236,7 +229,6 @@ exception is PartitionKeyRangeGoneException || exception, exceptionSubStatus); } -#pragma warning restore SA1505 // Opening braces should not be followed by blank line else { exceptionToThrow = ServiceUnavailableException.Create(exceptionSubStatus, innerException: exception); diff --git a/Microsoft.Azure.Cosmos/src/direct/GoneOnlyRequestRetryPolicy.cs b/Microsoft.Azure.Cosmos/src/direct/GoneOnlyRequestRetryPolicy.cs index 8904765a89..cc28e1979e 100644 --- a/Microsoft.Azure.Cosmos/src/direct/GoneOnlyRequestRetryPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/direct/GoneOnlyRequestRetryPolicy.cs @@ -17,9 +17,7 @@ internal class GoneOnlyRequestRetryPolicy : private const int backoffMultiplier = 2; private const int initialBackoffTimeInSeconds = 1; -#pragma warning disable IDE0044 // Add readonly modifier private Stopwatch durationTimer = new Stopwatch(); -#pragma warning restore IDE0044 // Add readonly modifier private readonly TimeSpan retryTimeout; private int currentBackoffTimeInSeconds; diff --git a/Microsoft.Azure.Cosmos/src/direct/Helpers.cs b/Microsoft.Azure.Cosmos/src/direct/Helpers.cs index 364209ea82..d33e71b2e7 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Helpers.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Helpers.cs @@ -52,9 +52,7 @@ public static string RemoveSigInSASTokenForTracingPayload(string sASToken) try { string pattern = "sig=.*?[&|\"]"; -#pragma warning disable SA1122 // Use string.Empty for empty strings string replacement = ""; -#pragma warning restore SA1122 // Use string.Empty for empty strings string result = Regex.Replace(sASToken, pattern, replacement, RegexOptions.IgnoreCase); return result; diff --git a/Microsoft.Azure.Cosmos/src/direct/HexStringUtility.cs b/Microsoft.Azure.Cosmos/src/direct/HexStringUtility.cs index 7e14436709..6ac0532bec 100644 --- a/Microsoft.Azure.Cosmos/src/direct/HexStringUtility.cs +++ b/Microsoft.Azure.Cosmos/src/direct/HexStringUtility.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------ +//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ @@ -24,4 +24,4 @@ internal static byte[] HexStringToBytes(string hexString) return bytes; } } -} \ No newline at end of file +} diff --git a/Microsoft.Azure.Cosmos/src/direct/HttpConstants.cs b/Microsoft.Azure.Cosmos/src/direct/HttpConstants.cs index 417d60c3cb..00fd2edc1e 100644 Binary files a/Microsoft.Azure.Cosmos/src/direct/HttpConstants.cs and b/Microsoft.Azure.Cosmos/src/direct/HttpConstants.cs differ diff --git a/Microsoft.Azure.Cosmos/src/direct/HttpException.cs b/Microsoft.Azure.Cosmos/src/direct/HttpException.cs index 71d4a32d0e..842bcd025f 100644 --- a/Microsoft.Azure.Cosmos/src/direct/HttpException.cs +++ b/Microsoft.Azure.Cosmos/src/direct/HttpException.cs @@ -27,15 +27,35 @@ namespace Microsoft.Azure.Documents public #endif class DocumentClientException : Exception +#if COSMOSCLIENT + , ICloneable +#endif { private Error error; private SubStatusCodes? substatus = null; -#pragma warning disable IDE0044 // Add readonly modifier private INameValueCollection responseHeaders; private string rawErrorMessage; private Boolean rawErrorMessageOnly; private bool skippingStackTraceCapture = false; -#pragma warning restore IDE0044 // Add readonly modifier + + + private readonly static HashSet SensitiveHeaders = new HashSet(StringComparer.OrdinalIgnoreCase) + { + HttpConstants.HttpHeaders.Authorization, + HttpConstants.HttpHeaders.FabricS2SToken, + HttpConstants.HttpHeaders.OriginalAadToken, + HttpConstants.HttpHeaders.GatewaySignature, + HttpConstants.HttpHeaders.SignedOboToken, + HttpConstants.HttpHeaders.ProxyAuthenticate, + HttpConstants.HttpHeaders.ProxyAuthorization, + }; + + private readonly static List SensitiveWordsInHeader = new List + { + "authorization", + "token", + "signature" + }; internal DocumentClientException(Error errorResource, HttpResponseHeaders responseHeaders, @@ -444,37 +464,38 @@ public override string Message } } + /// + /// Gets a public-facing message that describes the current exception from the Azure Cosmos DB service. + /// + /// + /// This property is intended for external consumption and differs from the Message property in that: + /// - It excludes internal diagnostic information like RequestStatistics for security/privacy reasons + /// - It maintains the same format with RequestUri and UserAgent information + /// - It's used in scenarios where exception details are exposed to external callers + /// + /// Historical context: RequestStatistics was removed from this property to prevent leaking internal + /// diagnostic information in public-facing error messages while keeping it available in the + /// internal Message property for debugging purposes. + /// internal virtual string PublicMessage { get { - string requestStatisticsMessage = this.RequestStatistics == null ? string.Empty : this.RequestStatistics.ToString(); if (this.RequestUri != null) { return string.Format(CultureInfo.CurrentUICulture, RMResources.ExceptionMessageAddRequestUri, base.Message, this.RequestUri.PathAndQuery, - requestStatisticsMessage, + string.Empty, CustomTypeExtensions.GenerateBaseUserAgentString()); } else { - if (string.IsNullOrEmpty(requestStatisticsMessage)) - { - return string.Format(CultureInfo.CurrentCulture, - "{0}, {1}", - base.Message, - CustomTypeExtensions.GenerateBaseUserAgentString()); - } - else - { - return string.Format(CultureInfo.CurrentUICulture, - "{0}, {1}, {2}", - base.Message, - requestStatisticsMessage, - CustomTypeExtensions.GenerateBaseUserAgentString()); - } + return string.Format(CultureInfo.CurrentCulture, + "{0}, {1}", + base.Message, + CustomTypeExtensions.GenerateBaseUserAgentString()); } } } @@ -559,7 +580,7 @@ private static string MessageWithActivityId(string message, string activityIdFro // If we're making this exception on the client side using the message from the Gateway, // the message may already have activityId stamped in it. If so, just use the message as-is - if (message.Contains(activityId)) + if (message == null || message.Contains(activityId)) { return message; } @@ -579,6 +600,11 @@ private static string SerializeHTTPResponseHeaders(HttpResponseHeaders responseH foreach (KeyValuePair> pair in responseHeaders) { + if (IsSensitiveHeader(pair.Key)) + { + continue; + } + foreach (string value in pair.Value) { result.Append(string.Format(CultureInfo.InvariantCulture, @@ -655,6 +681,11 @@ private static string SerializeHTTPResponseHeaders(INameValueCollection response foreach (Tuple item in items) { + if (IsSensitiveHeader(item.Item1)) + { + continue; + } + result.Append(string.Format(CultureInfo.InvariantCulture, "\"{0}\": \"{1}\",{2}", item.Item1, @@ -665,5 +696,27 @@ private static string SerializeHTTPResponseHeaders(INameValueCollection response result.Append("}"); return result.ToString(); } + + private static bool IsSensitiveHeader(string headerName) + { + if (string.IsNullOrEmpty(headerName)) + { + return false; + } + + if (SensitiveHeaders.Contains(headerName)) + { + return true; + } + + return SensitiveWordsInHeader.Any(keyword => headerName.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0); + } + +#if COSMOSCLIENT + public object Clone() + { + return this.MemberwiseClone(); + } +#endif } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/direct/HttpTransportClient.cs b/Microsoft.Azure.Cosmos/src/direct/HttpTransportClient.cs index a37905bc15..d53060d743 100644 --- a/Microsoft.Azure.Cosmos/src/direct/HttpTransportClient.cs +++ b/Microsoft.Azure.Cosmos/src/direct/HttpTransportClient.cs @@ -328,6 +328,7 @@ private HttpRequestMessage PrepareHttpMessage( HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.EmitVerboseTracesInQuery, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.CanCharge, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.CanThrottle, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.IsRequestIgnoredForAutoscaleReporting, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.EnableLowPrecisionOrderBy, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.EnableLogging, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.IsReadOnlyScript, request); @@ -485,6 +486,7 @@ private HttpRequestMessage PrepareHttpMessage( HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.IsOfferStorageRefreshRequest, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.IsServerlessStorageRefreshRequest, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.IsInternalServerlessRequest, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.AllowInternalServerlessOfferRead, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.UpdateMaxThroughputEverProvisioned, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.TruncateMergeLogRequest, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.AllowRequestWithoutInstanceId, request); @@ -504,6 +506,7 @@ private HttpRequestMessage PrepareHttpMessage( HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.SkipAdjustThroughputFractionsForOfferReplace, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.IsMigratedFixedCollection, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.SetMasterResourcesDeletionPending, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.CreatePKRangesWithStatusOffline, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.HighPriorityForcedBackup, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.EnableConflictResolutionPolicyUpdate, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.AllowDocumentReadsInOfflineRegion, request); @@ -518,6 +521,15 @@ private HttpRequestMessage PrepareHttpMessage( HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.PopulateVectorIndexAggregateProgress, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.ReadGlobalCommittedData, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.IsSoftDeletionOrRecoveryOperation, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.WorkloadId, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.IsEmbeddingGeneratorRequest, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.BypassSoftDeletionBlocking, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.IsStrongConsistencyStoreClient, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.ShouldProcessOnlyInHubRegion, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.OriginalAuthorizationTokenType, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.DistributedTransactionId, request); + HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, request); // Set the CollectionOperation TransactionId if present // Currently only being done for SharedThroughputTransactionHandler in the collection create path @@ -646,6 +658,7 @@ private HttpRequestMessage PrepareHttpMessage( case OperationType.GetFederationConfigurations: case OperationType.GetStorageServiceConfigurations: case OperationType.XPDatabaseAccountMetaData: + case OperationType.GetRegionalConfigurations: // IfModifiedSince and A_IM is already added above HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.ETag, request); HttpTransportClient.AddHeader(httpRequestMessage.Headers, HttpConstants.HttpHeaders.IfMatch, request); @@ -708,6 +721,13 @@ private HttpRequestMessage PrepareHttpMessage( httpRequestMessage.RequestUri = HttpTransportClient.GetRootOperationUri(physicalAddress, resourceOperation.operationType); httpRequestMessage.Method = HttpMethod.Get; break; + + case OperationType.GetAzureRbacAccessCheck: + httpRequestMessage.RequestUri = physicalAddress; + httpRequestMessage.Method = HttpMethod.Post; + Debug.Assert(clonedStream != null); + httpRequestMessage.Content = new StreamContent(clonedStream); + break; #endif default: @@ -777,6 +797,8 @@ internal static Uri GetResourceFeedUri(ResourceType resourceType, Uri physicalAd return HttpTransportClient.GetEncryptionScopeFeedUri(physicalAddress, request); case ResourceType.AuthPolicyElement: return HttpTransportClient.GetAuthPolicyElementFeedUri(physicalAddress, request); + case ResourceType.AzureRbac: + return HttpTransportClient.GetAzureRbacFeedUri(physicalAddress, request); case ResourceType.SystemDocument: return HttpTransportClient.GetSystemDocumentFeedUri(physicalAddress, request); case ResourceType.PartitionedSystemDocument: @@ -845,6 +867,8 @@ internal static Uri GetResourceEntryUri(ResourceType resourceType, Uri physicalA return HttpTransportClient.GetAuthPolicyElementEntryUri(physicalAddress, request); case ResourceType.InteropUser: return HttpTransportClient.GetInteropUserEntryUri(physicalAddress, request); + case ResourceType.AzureRbac: + return HttpTransportClient.GetAzureRbacEntryUri(physicalAddress, request); case ResourceType.SystemDocument: return HttpTransportClient.GetSystemDocumentEntryUri(physicalAddress, request); case ResourceType.PartitionedSystemDocument: @@ -1078,6 +1102,16 @@ private static Uri GetInteropUserEntryUri(Uri baseAddress, DocumentServiceReques return new Uri(baseAddress, PathsHelper.GeneratePath(ResourceType.InteropUser, request, isFeed: false)); } + private static Uri GetAzureRbacFeedUri(Uri baseAddress, DocumentServiceRequest request) + { + return new Uri(baseAddress, PathsHelper.GeneratePath(ResourceType.AzureRbac, request, isFeed: true)); + } + + private static Uri GetAzureRbacEntryUri(Uri baseAddress, DocumentServiceRequest request) + { + return new Uri(baseAddress, PathsHelper.GeneratePath(ResourceType.AzureRbac, request, isFeed: false)); + } + private static Uri GetSystemDocumentFeedUri(Uri baseAddress, DocumentServiceRequest request) { return new Uri(baseAddress, PathsHelper.GeneratePath(ResourceType.SystemDocument, request, isFeed: true)); @@ -1124,12 +1158,9 @@ public static Task ProcessHttpResponse(string resourceAddress, st { InternalServerErrorException exception = new InternalServerErrorException( - string.Format(CultureInfo.CurrentUICulture, - RMResources.ExceptionMessage, - RMResources.InvalidBackendResponse), + string.Format(CultureInfo.CurrentUICulture, RMResources.ExceptionMessage, RMResources.InvalidBackendResponse), physicalAddress); - exception.Headers.Set(HttpConstants.HttpHeaders.ActivityId, - activityId); + exception.Headers.Set(HttpConstants.HttpHeaders.ActivityId, activityId); exception.Headers.Add(HttpConstants.HttpHeaders.RequestValidationFailure, "1"); throw exception; } diff --git a/Microsoft.Azure.Cosmos/src/direct/HttpUtility.cs b/Microsoft.Azure.Cosmos/src/direct/HttpUtility.cs index d769606e65..3d8aaa8ab9 100644 --- a/Microsoft.Azure.Cosmos/src/direct/HttpUtility.cs +++ b/Microsoft.Azure.Cosmos/src/direct/HttpUtility.cs @@ -4,6 +4,7 @@ namespace Microsoft.Azure.Documents { using System; + using System.Buffers; using System.Globalization; using System.IO; using System.Text; @@ -14,9 +15,7 @@ namespace Microsoft.Azure.Documents internal sealed class HttpUtility { // Fields -#pragma warning disable IDE0044 // Add readonly modifier private static char[] s_entityEndingChars = new char[] { ';', '&' }; -#pragma warning restore IDE0044 // Add readonly modifier // Methods internal static string AspCompatUrlEncode(string s) @@ -36,7 +35,7 @@ internal static string AspCompatUrlEncode(string s) internal static string CollapsePercentUFromStringInternal(string s, Encoding e) { int length = s.Length; - UrlDecoder decoder = new UrlDecoder(length, e); + using UrlDecoder decoder = new UrlDecoder(length, e); if (s.IndexOf("%u", StringComparison.Ordinal) == -1) { return s; @@ -352,7 +351,7 @@ private static byte[] UrlDecodeBytesFromBytesInternal(byte[] buf, int offset, in private static string UrlDecodeStringFromBytesInternal(byte[] buf, int offset, int count, Encoding e) { - UrlDecoder decoder = new UrlDecoder(count, e); + using UrlDecoder decoder = new UrlDecoder(count, e); for (int i = 0; i < count; i++) { int index = offset + i; @@ -395,7 +394,7 @@ private static string UrlDecodeStringFromBytesInternal(byte[] buf, int offset, i private static string UrlDecodeStringFromStringInternal(string s, Encoding e) { int length = s.Length; - UrlDecoder decoder = new UrlDecoder(length, e); + using UrlDecoder decoder = new UrlDecoder(length, e); for (int i = 0; i < length; i++) { char ch = s[i]; @@ -754,15 +753,13 @@ public static string UrlPathEncode(string str) } // Nested Types - private class UrlDecoder + private sealed class UrlDecoder : IDisposable { // Fields -#pragma warning disable IDE0044 // Add readonly modifier private int _bufferSize; private byte[] _byteBuffer; private char[] _charBuffer; private Encoding _encoding; -#pragma warning restore IDE0044 // Add readonly modifier private int _numBytes; private int _numChars; @@ -771,14 +768,28 @@ internal UrlDecoder(int bufferSize, Encoding encoding) { this._bufferSize = bufferSize; this._encoding = encoding; - this._charBuffer = new char[bufferSize]; + this._charBuffer = ArrayPool.Shared.Rent(bufferSize); + } + + public void Dispose() + { + if (this._charBuffer != null) + { + ArrayPool.Shared.Return(this._charBuffer); + this._charBuffer = null; + } + if (this._byteBuffer != null) + { + ArrayPool.Shared.Return(this._byteBuffer); + this._byteBuffer = null; + } } internal void AddByte(byte b) { if (this._byteBuffer == null) { - this._byteBuffer = new byte[this._bufferSize]; + this._byteBuffer = ArrayPool.Shared.Rent(this._bufferSize); } this._byteBuffer[this._numBytes++] = b; } diff --git a/Microsoft.Azure.Cosmos/src/direct/IClientSideRequestStatistics.cs b/Microsoft.Azure.Cosmos/src/direct/IClientSideRequestStatistics.cs index c3b5895918..d8a2e6036d 100644 --- a/Microsoft.Azure.Cosmos/src/direct/IClientSideRequestStatistics.cs +++ b/Microsoft.Azure.Cosmos/src/direct/IClientSideRequestStatistics.cs @@ -53,8 +53,5 @@ void RecordHttpException(HttpRequestMessage request, ResourceType resourceType, DateTime requestStartTimeUtc); } -#pragma warning disable SA1518 // Use line endings correctly at end of file } - -#pragma warning restore SA1518 // Use line endings correctly at end of file \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/direct/IConnection.cs b/Microsoft.Azure.Cosmos/src/direct/IConnection.cs index 77d6a528a9..af2c3d46fa 100644 --- a/Microsoft.Azure.Cosmos/src/direct/IConnection.cs +++ b/Microsoft.Azure.Cosmos/src/direct/IConnection.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------ +//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ @@ -34,4 +34,4 @@ Task WriteRequestAsync( void NotifyConnectionStatus(bool isCompleted, bool isReadRequest = false); } -} \ No newline at end of file +} diff --git a/Microsoft.Azure.Cosmos/src/direct/IServiceConfigurationReaderExtension.cs b/Microsoft.Azure.Cosmos/src/direct/IServiceConfigurationReaderExtension.cs index c947dedb9a..fab63075cf 100644 --- a/Microsoft.Azure.Cosmos/src/direct/IServiceConfigurationReaderExtension.cs +++ b/Microsoft.Azure.Cosmos/src/direct/IServiceConfigurationReaderExtension.cs @@ -12,5 +12,7 @@ namespace Microsoft.Azure.Documents internal interface IServiceConfigurationReaderExtension : IServiceConfigurationReader { IServiceRetryParams TryGetServiceRetryParams(DocumentServiceRequest documentServiceRequest); + + bool TryGetConsistencyLevel(DocumentServiceRequest documentServiceRequest, out ConsistencyLevel consistencyLevel); } } diff --git a/Microsoft.Azure.Cosmos/src/direct/IServiceConfigurationReaderVnext.cs b/Microsoft.Azure.Cosmos/src/direct/IServiceConfigurationReaderVnext.cs new file mode 100644 index 0000000000..95b73590e6 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/direct/IServiceConfigurationReaderVnext.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Documents +{ + /// + /// Interface extension for IServiceConfigurationReader for new boolean flag for N-Region Synchronous Commit. + /// Instead of directly adding to the interface this is needed because the way compute + /// uses SDK is using a V3 OSS clone of our public v3 repo. So any change in the interface triggers a change in OSS/V3 codepath. + /// + internal interface IServiceConfigurationReaderVnext : IServiceConfigurationReader + { + /// + /// Enable N-Region Synchronous Commit Feature + /// + bool EnableNRegionSynchronousCommit { get; } + } +} diff --git a/Microsoft.Azure.Cosmos/src/direct/ISessionRetryOptions.cs b/Microsoft.Azure.Cosmos/src/direct/ISessionRetryOptions.cs index c6abefc1e6..1b5288a713 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ISessionRetryOptions.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ISessionRetryOptions.cs @@ -1,11 +1,9 @@ -namespace Microsoft.Azure.Documents +namespace Microsoft.Azure.Documents { using System; internal interface ISessionRetryOptions -#pragma warning disable SA1505 // Opening braces should not be followed by blank line { -#pragma warning restore SA1505 // Opening braces should not be followed by blank line /// /// Sets the minimum retry time for 404/1002 retries within each region for read and write operations. @@ -19,13 +17,11 @@ internal interface ISessionRetryOptions /// region is at least the min. in-region retry time. /// int MaxInRegionRetryCount { get; } -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row /// /// hints which guide SDK-internal retry policies on how early to switch retries to a different region. /// Boolean RemoteRegionPreferred { get; } -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/direct/IStoreClientFactory.cs b/Microsoft.Azure.Cosmos/src/direct/IStoreClientFactory.cs index 9e2eede3f3..4f90574148 100644 --- a/Microsoft.Azure.Cosmos/src/direct/IStoreClientFactory.cs +++ b/Microsoft.Azure.Cosmos/src/direct/IStoreClientFactory.cs @@ -19,7 +19,6 @@ StoreClient CreateStoreClient( bool useMultipleWriteLocations = false, bool detectClientConnectivityIssues = false, bool enableReplicaValidation = false, - AccountConfigurationProperties accountConfigurationProperties = null, ISessionRetryOptions sessionRetryOptions = null); IConnectionStateListener GetConnectionStateListener(); diff --git a/Microsoft.Azure.Cosmos/src/direct/JSonSerializable.cs b/Microsoft.Azure.Cosmos/src/direct/JSonSerializable.cs index 8f08535f5b..f9e1e0501e 100644 --- a/Microsoft.Azure.Cosmos/src/direct/JSonSerializable.cs +++ b/Microsoft.Azure.Cosmos/src/direct/JSonSerializable.cs @@ -179,11 +179,26 @@ public virtual void LoadFrom(JsonReader reader, JsonSerializerSettings serialize /// The type of the returning object. /// The stream to load from. /// The object loaded from the specified stream. + /// If the stream is null. + /// If the stream is empty. public static T LoadFrom(Stream stream) where T : JsonSerializable, new() { return LoadFrom(stream, null); } + /// + /// Loads the object from the specified string in the Azure Cosmos DB service. + /// + /// The type of the returning object + /// The string to load from. + /// The object loaded from the specified string. + /// If the serialized string is null. + /// If the serialized string is empty. + public static T LoadFrom(string serialized) where T : JsonSerializable, new() + { + return LoadFrom(serialized, null); + } + /// /// Loads the object from the specified stream. /// diff --git a/Microsoft.Azure.Cosmos/src/direct/LinuxSystemUtilizationReader.cs b/Microsoft.Azure.Cosmos/src/direct/LinuxSystemUtilizationReader.cs index 030920f668..eff52c7e65 100644 --- a/Microsoft.Azure.Cosmos/src/direct/LinuxSystemUtilizationReader.cs +++ b/Microsoft.Azure.Cosmos/src/direct/LinuxSystemUtilizationReader.cs @@ -233,20 +233,13 @@ private class ProcMemInfoFileParser public ProcMemInfoFileParser() : this(DefaultProcMemInfoFilePath) { } -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row - -#pragma warning disable CS1572 // XML comment has a param tag, but there is no parameter by that name /// /// Allows customization of the proc stat file path to allow testing this on Non-Linux machines /// /// /// -#pragma warning disable CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do) internal ProcMemInfoFileParser(string procMemInfoFilePath) -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row -#pragma warning restore CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do) -#pragma warning restore CS1572 // XML comment has a param tag, but there is no parameter by that name { if (String.IsNullOrWhiteSpace(procMemInfoFilePath)) { diff --git a/Microsoft.Azure.Cosmos/src/direct/LoadBalancingChannel.cs b/Microsoft.Azure.Cosmos/src/direct/LoadBalancingChannel.cs index 7d22937cb3..0840ffb498 100644 --- a/Microsoft.Azure.Cosmos/src/direct/LoadBalancingChannel.cs +++ b/Microsoft.Azure.Cosmos/src/direct/LoadBalancingChannel.cs @@ -66,6 +66,8 @@ public LoadBalancingChannel( channelProperties.EnableChannelMultiplexing, channelProperties.MemoryStreamPool, channelProperties.RemoteCertificateValidationCallback, + channelProperties.ClientCertificateFunction, + channelProperties.ClientCertificateFailureHandler, channelProperties.DnsResolutionFunction); this.partitions = new LoadBalancingPartition[channelProperties.PartitionCount]; for (int i = 0; i < this.partitions.Length; i++) diff --git a/Microsoft.Azure.Cosmos/src/direct/LocationNames.cs b/Microsoft.Azure.Cosmos/src/direct/LocationNames.cs index 2fa5cafeca..fc4d03ce5d 100644 --- a/Microsoft.Azure.Cosmos/src/direct/LocationNames.cs +++ b/Microsoft.Azure.Cosmos/src/direct/LocationNames.cs @@ -483,8 +483,27 @@ static class LocationNames /// Name of the Azure East US 3 region in the Azure Cosmos DB service. /// public const string EastUS3 = "East US 3"; + + /// + /// Name of the Azure Northeast US 5 region in the Azure Cosmos DB service. + /// + public const string NortheastUS5 = "Northeast US 5"; + + /// + /// Name of the Azure India South Central region in the Azure Cosmos DB service. + /// + public const string IndiaSouthCentral = "India South Central"; + + /// + /// Name of the Azure Singapore Central region in the Azure Cosmos DB service. + /// + public const string SingaporeCentral = "Singapore Central"; + + /// + /// Name of the Azure Singapore North region in the Azure Cosmos DB service. + /// + public const string SingaporeNorth = "Singapore North"; } -#pragma warning disable SA1518 // Use line endings correctly at end of file } @@ -492,4 +511,6 @@ static class LocationNames -#pragma warning restore SA1518 // Use line endings correctly at end of file \ No newline at end of file + + + diff --git a/Microsoft.Azure.Cosmos/src/direct/MaterializedViewDefinition.cs b/Microsoft.Azure.Cosmos/src/direct/MaterializedViewDefinition.cs index cf12feb0b3..bdc216e0d6 100644 --- a/Microsoft.Azure.Cosmos/src/direct/MaterializedViewDefinition.cs +++ b/Microsoft.Azure.Cosmos/src/direct/MaterializedViewDefinition.cs @@ -80,6 +80,33 @@ public string ContainerType } } + [JsonProperty(PropertyName = Constants.Properties.ThroughputBucketForBuild, NullValueHandling = NullValueHandling.Ignore)] + public int? ThroughputBucketForBuild + { + get + { + return base.GetValue(Constants.Properties.ThroughputBucketForBuild); + } + set + { + this.SetValue(Constants.Properties.ThroughputBucketForBuild, value); + } + } + + // This property is used for identifying the status of materialized view + [JsonProperty(PropertyName = Constants.Properties.MaterializedViewStatus, NullValueHandling = NullValueHandling.Ignore)] + public string Status + { + get + { + return base.GetValue(Constants.Properties.MaterializedViewStatus); + } + set + { + this.SetValue(Constants.Properties.MaterializedViewStatus, value); + } + } + public object Clone() { MaterializedViewDefinition cloned = new MaterializedViewDefinition() @@ -87,7 +114,9 @@ public object Clone() SourceCollectionRid = this.SourceCollectionRid, Definition = this.Definition, ApiSpecificDefinition = this.ApiSpecificDefinition, - ContainerType = this.ContainerType + ContainerType = this.ContainerType, + ThroughputBucketForBuild = this.ThroughputBucketForBuild, + Status = this.Status }; return cloned; } diff --git a/Microsoft.Azure.Cosmos/src/direct/MaterializedViews.cs b/Microsoft.Azure.Cosmos/src/direct/MaterializedViews.cs index 96c2bd5da9..c8ddffae77 100644 --- a/Microsoft.Azure.Cosmos/src/direct/MaterializedViews.cs +++ b/Microsoft.Azure.Cosmos/src/direct/MaterializedViews.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------ +//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace Microsoft.Azure.Documents @@ -44,4 +44,4 @@ public object Clone() return cloned; } } -} \ No newline at end of file +} diff --git a/Microsoft.Azure.Cosmos/src/direct/MaterializedViewsProperties.cs b/Microsoft.Azure.Cosmos/src/direct/MaterializedViewsProperties.cs new file mode 100644 index 0000000000..d9ece8d0a7 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/direct/MaterializedViewsProperties.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Documents +{ + using System; + using Newtonsoft.Json; +#if COSMOSCLIENT + internal +#else + internal +#endif + + sealed class MaterializedViewsProperties : JsonSerializable, ICloneable + { + [JsonProperty(PropertyName = Constants.Properties.ThroughputBucketForBuild, NullValueHandling = NullValueHandling.Ignore)] + public int ThroughputBucketForBuild + { + get + { + return base.GetValue(Constants.Properties.ThroughputBucketForBuild); + } + set + { + this.SetValue(Constants.Properties.ThroughputBucketForBuild, value); + } + } + + public object Clone() + { + MaterializedViewsProperties cloned = new MaterializedViewsProperties() + { + ThroughputBucketForBuild = this.ThroughputBucketForBuild + }; + return cloned; + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/direct/NetUtil.cs b/Microsoft.Azure.Cosmos/src/direct/NetUtil.cs index 290e10e767..f57d50cf9c 100644 --- a/Microsoft.Azure.Cosmos/src/direct/NetUtil.cs +++ b/Microsoft.Azure.Cosmos/src/direct/NetUtil.cs @@ -8,6 +8,8 @@ namespace Microsoft.Azure.Documents using System.Net.NetworkInformation; using System.Net.Sockets; using Microsoft.Azure.Cosmos.Core.Trace; + using System.Linq; + using System.Collections.Generic; #if !(NETSTANDARD15 || NETSTANDARD16) using System.Configuration; #endif @@ -130,6 +132,116 @@ public static bool GetIPv6ServiceTunnelAddress(bool isEmulated, out IPAddress ip return false; } + public static bool TryGetLocalIPv6Address(bool isEmulated, out IPAddress address) + { + address = null; + if (isEmulated) + { + address = IPAddress.IPv6Loopback; + return true; + } + + NetworkInterface[] niList = NetworkInterface.GetAllNetworkInterfaces(); + return TryGetEthernetIPv6Address(niList, out address); + } + + /// + /// Selects the best IPv6 address from the provided network interfaces based on a scoring algorithm. + /// This method is internal to allow testing with controlled network interface data. + /// + /// Array of network interfaces to evaluate. + /// A tuple indicating success and the selected IPv6 address. + internal static bool TryGetEthernetIPv6Address(NetworkInterface[] networkInterfaces, out IPAddress address) + { + List<(IPAddress Ip, NetworkInterface Nic, int Score)> candidates = new(); + + address = null; + + foreach (NetworkInterface ni in networkInterfaces) + { + if (ni.OperationalStatus != OperationalStatus.Up || ni.NetworkInterfaceType != NetworkInterfaceType.Ethernet || ni.IsReceiveOnly) + { + continue; + } + + IPInterfaceProperties props = ni.GetIPProperties(); + + bool hasIpv6Gateway = props.GatewayAddresses + .Any(g => g.Address.AddressFamily == AddressFamily.InterNetworkV6 && + !g.Address.IsIPv6LinkLocal); + + foreach (UnicastIPAddressInformation uip in props.UnicastAddresses) + { + if (uip.Address.AddressFamily != AddressFamily.InterNetworkV6) + { + DefaultTrace.TraceInformation("{0} is skipped because it is not IPv6.", uip.Address.ToString()); + continue; + } + + // Only use addresses that are currently preferred + if (uip.DuplicateAddressDetectionState != DuplicateAddressDetectionState.Preferred) + { + DefaultTrace.TraceInformation("{0} is skipped because it is not preferred.", uip.Address.ToString()); + continue; + } + + // Skip transient (temporary/privacy) addresses + if (uip.IsTransient) + { + DefaultTrace.TraceInformation("{0} is skipped because it is temporary/privacy.", uip.Address.ToString()); + continue; + } + + if (!uip.IsDnsEligible) + { + DefaultTrace.TraceInformation("{0} is skipped because it is not DNS eligible.", uip.Address.ToString()); + continue; + } + + IPAddress ip = uip.Address; + + if (ip.IsIPv6LinkLocal || ip.IsIPv6Multicast) + { + DefaultTrace.TraceInformation("{0} is skipped because it is link-local or multicast.", uip.Address.ToString()); + continue; + } + + int score = 0; + + // Prefer NICs that have an IPv6 default gateway + if (hasIpv6Gateway) + score += 100; + + // Prefer global over ULA (fd00::/8) + if (!IsUniqueLocal(ip)) + score += 10; + + candidates.Add((ip, ni, score)); + } + } + + if (candidates.Count == 0) + { + DefaultTrace.TraceInformation("No suitable IPv6 address found."); + return false; + } + + (IPAddress Ip, NetworkInterface Nic, int Score) best = candidates + .OrderByDescending(c => c.Score) + .FirstOrDefault(); + + address = best.Ip; + + return true; + } + + private static bool IsUniqueLocal(IPAddress ip) + { + // ULA: fc00::/7 + byte[] bytes = ip.GetAddressBytes(); + return (bytes[0] & 0xfe) == 0xfc; + } + private static bool IsServiceTunneledIPAddress(IPAddress ipAddress) { byte[] ipAddressBytes = ipAddress.GetAddressBytes(); diff --git a/Microsoft.Azure.Cosmos/src/direct/OfferContentV2.cs b/Microsoft.Azure.Cosmos/src/direct/OfferContentV2.cs index 53f8077659..802b1b3a30 100644 --- a/Microsoft.Azure.Cosmos/src/direct/OfferContentV2.cs +++ b/Microsoft.Azure.Cosmos/src/direct/OfferContentV2.cs @@ -4,8 +4,9 @@ namespace Microsoft.Azure.Documents { - using System.Collections.ObjectModel; using Newtonsoft.Json; + using System; + using System.Collections.ObjectModel; /// /// Represents content properties tied to the Standard pricing tier for the Azure Cosmos DB service. @@ -24,6 +25,7 @@ sealed class OfferContentV2 : JsonSerializable private ThroughputDistributionPolicyType? throughputDistributionPolicy; private Collection throughputBuckets; private int? offerTargetThroughput; + private int? partitionCount; #endif /// @@ -98,6 +100,12 @@ internal OfferContentV2( { this.OfferTargetThroughput = offerTargetThroughput.Value; } + + int? partitionCount = content.PartitionCount; + if (partitionCount.HasValue) + { + this.PartitionCount = partitionCount.Value; + } } #endif } @@ -142,6 +150,12 @@ internal OfferContentV2( { this.OfferTargetThroughput = offerTargetThroughput.Value; } + + int? partitionCount = content.PartitionCount; + if (partitionCount.HasValue) + { + this.PartitionCount = partitionCount.Value; + } } #endif } @@ -294,11 +308,62 @@ internal OfferContentV2( { this.OfferTargetThroughput = offerTargetThroughput.Value; } + + int? partitionCount = contentV2.PartitionCount; + if (partitionCount.HasValue) + { + this.PartitionCount = partitionCount.Value; + } + } + + /// + /// Copy constructor with optional overrides for all properties except OfferThroughput. + /// + internal OfferContentV2( + OfferContentV2 source, + bool? offerIsRUPerMinuteThroughputEnabled = null, + bool? offerIsAutoScaleEnabled = null, + AutopilotSettings offerAutopilotSettings = null, + OfferMinimumThroughputParameters offerMinimumThroughputParameters = null, + double? backgroundTaskMaxAllowedThroughputPercent = null, + ThroughputDistributionPolicyType? throughputDistributionPolicy = null, + Collection throughputBuckets = null, + Collection physicalPartitionThroughputInfo = null, + int? offerTargetThroughput = null, + int? partitionCount = null) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + // Throughput is always copied from source + this.OfferThroughput = source.OfferThroughput; + + this.OfferIsRUPerMinuteThroughputEnabled = offerIsRUPerMinuteThroughputEnabled ?? source.OfferIsRUPerMinuteThroughputEnabled; + this.OfferIsAutoScaleEnabled = offerIsAutoScaleEnabled ?? source.OfferIsAutoScaleEnabled; + + if (offerAutopilotSettings != null) + { + this.OfferAutopilotSettings = new AutopilotSettings(offerAutopilotSettings); + } + else if (source.OfferAutopilotSettings != null) + { + this.OfferAutopilotSettings = new AutopilotSettings(source.OfferAutopilotSettings); + } + + this.OfferMinimumThroughputParameters = offerMinimumThroughputParameters != null ? new OfferMinimumThroughputParameters(offerMinimumThroughputParameters) : (source.OfferMinimumThroughputParameters != null ? new OfferMinimumThroughputParameters(source.OfferMinimumThroughputParameters) : null); + this.BackgroundTaskMaxAllowedThroughputPercent = backgroundTaskMaxAllowedThroughputPercent ?? source.BackgroundTaskMaxAllowedThroughputPercent; + this.ThroughputDistributionPolicy = throughputDistributionPolicy ?? source.ThroughputDistributionPolicy; + this.ThroughputBuckets = throughputBuckets ?? source.ThroughputBuckets; + this.PhysicalPartitionThroughputInfo = physicalPartitionThroughputInfo ?? source.PhysicalPartitionThroughputInfo; + this.OfferTargetThroughput = offerTargetThroughput ?? source.OfferTargetThroughput; + this.PartitionCount = partitionCount ?? source.PartitionCount; } /// /// Internal constructor accepting offer throughput, autopilot settings, minimum throughput parameters, bg task throughput percent, - /// throughput distribution policy, throughput buckets, and target throughput + /// throughput distribution policy, throughput buckets, target throughput, and partition count. /// internal OfferContentV2( int offerThroughput, @@ -309,7 +374,8 @@ internal OfferContentV2( double? bgTaskMaxAllowedThroughputPercent, ThroughputDistributionPolicyType? throughputDistributionPolicy, Collection throughputBuckets, - int? offerTargetThroughput = null) + int? offerTargetThroughput = null, + int? partitionCount = null) { this.OfferThroughput = offerThroughput; this.OfferIsRUPerMinuteThroughputEnabled = offerEnableRUPerMinuteThroughput; @@ -344,6 +410,11 @@ internal OfferContentV2( { this.OfferTargetThroughput = offerTargetThroughput.Value; } + + if (partitionCount.HasValue) + { + this.PartitionCount = partitionCount.Value; + } } internal OfferContentV2( @@ -373,6 +444,13 @@ internal OfferContentV2( { this.OfferTargetThroughput = offerTargetThroughput.Value; } + + int? partitionCount = content.PartitionCount; + if (partitionCount.HasValue) + { + this.PartitionCount = partitionCount.Value; + } + } } #endif @@ -560,7 +638,7 @@ internal OfferMinimumThroughputParameters OfferMinimumThroughputParameters /// as it would result in not deserializing autoscale settings correctly. /// /// - [JsonProperty(PropertyName = Constants.Properties.AutopilotSettings, DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty(PropertyName = Constants.Properties.AutopilotSettings, DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore)] internal AutopilotSettings OfferAutopilotSettings { get @@ -633,6 +711,29 @@ private set } } + /// + /// Represents partition count. + /// + [JsonProperty(PropertyName = Constants.Properties.PartitionCount, DefaultValueHandling = DefaultValueHandling.Ignore)] + internal int? PartitionCount + { + get + { + if (this.partitionCount == null) + { + this.partitionCount = base.GetValue(Constants.Properties.PartitionCount); + } + + return this.partitionCount; + } + private set + { + this.partitionCount = value; + base.SetValue(Constants.Properties.PartitionCount, this.partitionCount); + } + } + + internal override void OnSave() { base.OnSave(); diff --git a/Microsoft.Azure.Cosmos/src/direct/OfferV2.cs b/Microsoft.Azure.Cosmos/src/direct/OfferV2.cs index 902cc6f5ad..51e7406943 100644 --- a/Microsoft.Azure.Cosmos/src/direct/OfferV2.cs +++ b/Microsoft.Azure.Cosmos/src/direct/OfferV2.cs @@ -235,7 +235,8 @@ internal OfferV2( double? bgTaskMaxAllowedThroughputPercent, ThroughputDistributionPolicyType? throughputDistributionPolicy, Collection throughputBuckets, - int? offerTargetThroughput = null) + int? offerTargetThroughput = null, + int? partitionCount = null) : base(offer) { this.OfferType = string.Empty; @@ -250,7 +251,8 @@ internal OfferV2( bgTaskMaxAllowedThroughputPercent, throughputDistributionPolicy, throughputBuckets, - offerTargetThroughput); + offerTargetThroughput, + partitionCount); } #endif diff --git a/Microsoft.Azure.Cosmos/src/direct/OperationType.cs b/Microsoft.Azure.Cosmos/src/direct/OperationType.cs index ea7af81e23..59bdb6a92d 100644 --- a/Microsoft.Azure.Cosmos/src/direct/OperationType.cs +++ b/Microsoft.Azure.Cosmos/src/direct/OperationType.cs @@ -116,9 +116,40 @@ internal enum OperationType RelocateLeakedTentativeWrites = 70, - Last = 71, + // Operation type for checking if the backend is able to serve an external backup request. + ExternalPreBackup = 71, + + // Operation type for uploading external backups + ExternalBackup = 72, + + // Operation type for checking external backup status + CheckExternalBackupStatus = 73, + + // Operation type for restoring external backups + ExternalBackupRestore = 74, + + // Operation type for checking external backup restore status + CheckExternalBackupRestoreStatus = 75, + + // Distributed transaction operations + PrepareDistributedTransaction = 76, //this will not be used by Client SDKs +#endif + CommitDistributedTransaction = 77, + AbortDistributedTransaction = 78, + +#if !COSMOSCLIENT + // Operation type for cancelling external backup + CancelExternalBackup = 79, +#endif + +#if !COSMOSCLIENT + // Operation type for cancelling external backup restore + CancelExternalBackupRestore = 80, #endif + // Add new operation types above this + Last = 81, + // These names make it unclear what they map to in RequestOperationType. ExecuteJavaScript = -2, GetConfiguration = -8, @@ -149,6 +180,8 @@ internal enum OperationType ControllerBatchGetOutputV2 = -27, ControllerBatchWatchdogHealthCheckPing = -28, GetDatabaseAccountArtifactPermissions = -29, + GetRegionalConfigurations = -30, + GetAzureRbacAccessCheck = -31, #endif } @@ -227,7 +260,9 @@ public static bool IsWriteOperation(this OperationType type) type == OperationType.BatchApply || type == OperationType.Batch || type == OperationType.Upsert || - type == OperationType.CompleteUserTransaction + type == OperationType.CompleteUserTransaction || + type == OperationType.AbortDistributedTransaction || + type == OperationType.CommitDistributedTransaction #if !COSMOSCLIENT || type == OperationType.MasterInitiatedProgressCoordination || @@ -258,7 +293,12 @@ public static bool IsWriteOperation(this OperationType type) type == OperationType.Resume || type == OperationType.UpdatePartitionThroughput || type == OperationType.Truncate || - type == OperationType.RelocateLeakedTentativeWrites + type == OperationType.RelocateLeakedTentativeWrites || + type == OperationType.ExternalBackup || + type == OperationType.ExternalBackupRestore || + type == OperationType.PrepareDistributedTransaction || + type == OperationType.CancelExternalBackup || + type == OperationType.CancelExternalBackupRestore #endif ; } @@ -285,7 +325,10 @@ public static bool IsReadOperation(this OperationType type) type == OperationType.QueryPlan #if !COSMOSCLIENT || - type == OperationType.GetStorageAuthToken + type == OperationType.GetStorageAuthToken || + type == OperationType.ExternalPreBackup || + type == OperationType.CheckExternalBackupStatus || + type == OperationType.CheckExternalBackupRestoreStatus #endif ; } diff --git a/Microsoft.Azure.Cosmos/src/direct/PartitionKeyInternal.cs b/Microsoft.Azure.Cosmos/src/direct/PartitionKeyInternal.cs index d8910fdcea..806cd9ccc8 100644 --- a/Microsoft.Azure.Cosmos/src/direct/PartitionKeyInternal.cs +++ b/Microsoft.Azure.Cosmos/src/direct/PartitionKeyInternal.cs @@ -41,6 +41,8 @@ internal sealed class PartitionKeyInternal : IComparable, private static readonly Int32 HashV2EPKLength = 32; // UInt128.Length * 2 (UInt128 gives 16 bytes as output, each byte takes 2 chars after hex-encoding) + private static readonly int BytesPerPath = new Int128().Bytes.Length; + private static readonly JsonSerializer FromJsonStringSerializer = JsonSerializer.CreateDefault( new JsonSerializerSettings @@ -229,7 +231,8 @@ public static string GetMinInclusiveEffectivePartitionKey( int partitionIndex, int partitionCount, PartitionKeyDefinition partitionKeyDefinition, - bool useHashV2asDefault = false) + bool useHashV2asDefault = false, + bool enablePadding = false) { if (partitionKeyDefinition.Paths.Count > 0 && !(partitionKeyDefinition.Kind == PartitionKind.Hash || partitionKeyDefinition.Kind == PartitionKind.MultiHash)) { @@ -258,10 +261,7 @@ public static string GetMinInclusiveEffectivePartitionKey( switch (partitionKeyDefinition.Version ?? defaultPartitionKeyDefinitionVersion) { case PartitionKeyDefinitionVersion.V2: - Int128 val = MaxHashV2Value / partitionCount * partitionIndex; - byte[] bytes = val.Bytes; - Array.Reverse(bytes); - return HexConvert.ToHex(bytes, 0, bytes.Length); + return CalculateEffectivePartitionKeyHex(partitionIndex, partitionCount, partitionKeyDefinition, isMaxExclusive: false); case PartitionKeyDefinitionVersion.V1: return ToHexEncodedBinaryString( @@ -272,10 +272,7 @@ public static string GetMinInclusiveEffectivePartitionKey( } case PartitionKind.MultiHash: - Int128 max_val = MaxHashV2Value / partitionCount * partitionIndex; - byte[] max_bytes = max_val.Bytes; - Array.Reverse(max_bytes); - return HexConvert.ToHex(max_bytes, 0, max_bytes.Length); + return CalculateEffectivePartitionKeyHex(partitionIndex, partitionCount, partitionKeyDefinition, isMaxExclusive: false, enablePadding); default: throw new InternalServerErrorException("Unexpected PartitionKeyDefinitionKind"); @@ -286,7 +283,8 @@ public static string GetMaxExclusiveEffectivePartitionKey( int partitionIndex, int partitionCount, PartitionKeyDefinition partitionKeyDefinition, - bool useHashV2asDefault = false) + bool useHashV2asDefault = false, + bool enablePadding = false) { if (partitionKeyDefinition.Paths.Count > 0 && !(partitionKeyDefinition.Kind == PartitionKind.Hash || partitionKeyDefinition.Kind == PartitionKind.MultiHash)) { @@ -315,10 +313,7 @@ public static string GetMaxExclusiveEffectivePartitionKey( switch (partitionKeyDefinition.Version ?? defaultPartitionKeyDefinitionVersion) { case PartitionKeyDefinitionVersion.V2: - Int128 val = MaxHashV2Value / partitionCount * (partitionIndex + 1); - byte[] bytes = val.Bytes; - Array.Reverse(bytes); - return HexConvert.ToHex(bytes, 0, bytes.Length); + return CalculateEffectivePartitionKeyHex(partitionIndex, partitionCount, partitionKeyDefinition, isMaxExclusive: true); case PartitionKeyDefinitionVersion.V1: return ToHexEncodedBinaryString(new IPartitionKeyComponent[] { new NumberPartitionKeyComponent(uint.MaxValue / partitionCount * (partitionIndex + 1)) }); @@ -328,16 +323,62 @@ public static string GetMaxExclusiveEffectivePartitionKey( } case PartitionKind.MultiHash: - - Int128 max_val = MaxHashV2Value / partitionCount * (partitionIndex + 1); - byte[] max_bytes = max_val.Bytes; - Array.Reverse(max_bytes); - return HexConvert.ToHex(max_bytes, 0, max_bytes.Length); + return CalculateEffectivePartitionKeyHex(partitionIndex, partitionCount, partitionKeyDefinition, isMaxExclusive: true, enablePadding: enablePadding); default: throw new InternalServerErrorException("Unexpected PartitionKeyDefinitionKind"); } } + + public static void ValidateMultiHashPartitionKey(PartitionKeyDefinition partitionKeyDef) + { + if (partitionKeyDef.Paths.Count == 0) + { + throw new BadRequestException("MultiHash partition key must have at least one path"); + } + + if (partitionKeyDef.Paths.Count > 3) + { + throw new BadRequestException($"MultiHash partition key cannot have more than 3 paths"); + } + + foreach (string path in partitionKeyDef.Paths) + { + if (string.IsNullOrEmpty(path)) + { + throw new BadRequestException("MultiHash partition key paths cannot be empty"); + } + } + } + + private static string CalculateEffectivePartitionKeyHex( + int partitionIndex, + int partitionCount, + PartitionKeyDefinition partitionKeyDefinition, + bool isMaxExclusive, + bool enablePadding = false) + { + if (partitionKeyDefinition.Kind == PartitionKind.MultiHash && enablePadding) + { + ValidateMultiHashPartitionKey(partitionKeyDefinition); + } + + int index = isMaxExclusive ? partitionIndex + 1 : partitionIndex; + Int128 val = MaxHashV2Value / partitionCount * index; + byte[] bytes = val.Bytes; + Array.Reverse(bytes); + + if (partitionKeyDefinition.Kind == PartitionKind.MultiHash && enablePadding) + { + int pathCount = partitionKeyDefinition.Paths.Count; + byte[] result = new byte[pathCount * BytesPerPath]; + Array.Copy(bytes, 0, result, 0, BytesPerPath); + bytes = result; + } + + return HexConvert.ToHex(bytes, 0, bytes.Length); + } + public int CompareTo(PartitionKeyInternal other) { if (other == null) @@ -811,9 +852,7 @@ private static IReadOnlyList GetHashValueFromEPKForMultiHash(string epkV hashes.Add(new Int128(maxBytes)); } } -#pragma warning disable SA1108 else // The EPK has less values than Paths.Count in the PkDef, this is empty partitionkey. -#pragma warning restore SA1108 { hashes.Add(0); } diff --git a/Microsoft.Azure.Cosmos/src/direct/Paths.cs b/Microsoft.Azure.Cosmos/src/direct/Paths.cs index 06e57ee27c..e661797149 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Paths.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Paths.cs @@ -19,9 +19,7 @@ internal static class Paths public const string ReplicaOperations_ReportThroughputUtilization = "reportthroughpututilization"; public const string ReplicaOperations_BatchReportThroughputUtilization = "batchreportthroughpututilization"; public const string Operations_GetFederationConfigurations = "getfederationconfigurations"; -#pragma warning disable SA1310 // Field names should not contain underscore public const string Operations_GetRegionalConfigurations = "getregionalconfigurations"; -#pragma warning restore SA1310 // Field names should not contain underscore public const string Operations_GetConfiguration = "getconfiguration"; public const string Operations_GetDatabaseAccountConfigurations = "getdatabaseaccountconfigurations"; public const string Operations_GetMicrosoftFabricDatabaseAccountArtifactPermissions = "getdatabaseaccountartifactpermissions"; @@ -38,9 +36,10 @@ internal static class Paths public const string Operations_GetAadGroups = "getaadgroups"; public const string Operations_XPDatabaseAccountMetaData = "xpmetadata"; public const string Operations_MetadataCheckAccess = "metadatacheckaccess"; + public const string Operations_GetAzureRbacAccessCheck = "getazurerbacaccesscheck"; //databases namespace off of root------------------- - + // /subscriptions public const string SubscriptionsSegment = "subscriptions"; public const string SubscriptionsSegment_Root = Root + SubscriptionsSegment; @@ -270,6 +269,9 @@ internal static class Paths // FedURL/accounts/{id}/offers/{id} public const string FederationEndpoint_Offer_Root = FederationEndpoint_Offers_Root + "{" + OfferId + "}"; + // FedURL/accounts/{accountName}/operations/metadatacheckaccess + public const string FederationEndpoint_CheckMetaData_Access = FederationEndpoint_Root + "/" + OperationsPathSegment + "/" + MetadataCheckAccessPathSegment; + // /topology public const string TopologyPathSegment = "topology"; public const string Topology_Root = Root + "/" + TopologyPathSegment + "/"; @@ -373,6 +375,14 @@ internal static class Paths public const string InteropUserId = "interopuserId"; public const string InteropUser_Root = InteropUsers_Root + "{" + InteropUserId + "}"; + // /azureRbac + public const string AzureRbacPathSegment = "azurerbac"; + public const string AzureRbac_Root = Root + "/" + AzureRbacPathSegment + "/"; + + // /azureRbac/{} + public const string AzureRbacId = "azurerbacId"; + public const string AzureRbacResource_Root = AzureRbac_Root + "{" + AzureRbacId + "}"; + // /localemulator public const string LocalEmulatorPathSegment = "localemulator"; public const string LocalEmulator_Root = Root + "/" + LocalEmulatorPathSegment + "/"; diff --git a/Microsoft.Azure.Cosmos/src/direct/PathsHelper.cs b/Microsoft.Azure.Cosmos/src/direct/PathsHelper.cs index 27a032b7d0..d47a79d433 100644 --- a/Microsoft.Azure.Cosmos/src/direct/PathsHelper.cs +++ b/Microsoft.Azure.Cosmos/src/direct/PathsHelper.cs @@ -63,8 +63,8 @@ public static bool TryParsePathSegments( return true; } - if (!string.IsNullOrEmpty(resourceUrl) && resourceUrl.Contains(Paths.OperationsPathSegment) && - (resourceUrl.Contains(Paths.MetadataCheckAccessPathSegment))) + if (PathsHelper.TryParseRootOperation(resourceUrl, out string rootOperationName) && + string.Equals(rootOperationName, Paths.Operations_MetadataCheckAccess, StringComparison.OrdinalIgnoreCase)) { // For metadata check access the, the resource path is always root. isFeed = false; @@ -101,6 +101,52 @@ public static bool TryParsePathSegmentsWithDatabaseAndCollectionNames( parseDatabaseAndCollectionNames); } + /// + /// Tries to parse a root operation name from a resourcURL + /// + /// The format fo a URL will be: + /// operations/rootOperationName + /// + /// Document Service URL to parse. + /// Name of the root operation. + /// + public static bool TryParseRootOperation( + string resourceUrl, + out string rootOperationName) + { + rootOperationName = null; + + if (string.IsNullOrEmpty(resourceUrl)) + { + return false; + } + + resourceUrl = PathsHelper.RemoveAccountsSegment(resourceUrl); + + string[] segments = resourceUrl.Split(PathsHelper.PathSeparatorArray, StringSplitOptions.RemoveEmptyEntries); + + if (segments == null || segments.Length < 1) + { + return false; + } + + int uriSegmentsCount = segments.Length; + StringSegment segmentOne = new StringSegment(segments[uriSegmentsCount - 1]).Trim(PathsHelper.PathSeparatorArray); + StringSegment segmentTwo = new StringSegment(string.Empty); + if (uriSegmentsCount >= 2) + { + segmentTwo = new StringSegment(segments[uriSegmentsCount - 2]).Trim(PathsHelper.PathSeparatorArray); + } + + if (PathsHelper.IsRootOperation(segmentTwo, segmentOne)) + { + rootOperationName = segmentOne.GetString(); + return true; + } + + return false; + } + /// /// The output resourceId can be /// a: (Rid based) DgJ5AJeIfQABAAAAAAAAAPy3CWY= @@ -239,6 +285,13 @@ public static bool TryParsePathSegmentsWithDatabaseAndCollectionAndDocumentNames isNameBased = true; } } + else if (firstSegment.Equals(Paths.AzureRbacPathSegment, StringComparison.OrdinalIgnoreCase)) + { + if (!ResourceId.TryParse(segments[1], out rid) || !rid.IsAzureRbacId) + { + isNameBased = true; + } + } if (isNameBased) { @@ -349,9 +402,7 @@ public static bool TryParsePathSegmentsWithDatabaseAndCollectionAndOperationName string resourceUrl, out string resourcePath, out string resourceIdOrFullName, -#pragma warning disable CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do) out bool isNameBased, -#pragma warning restore CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do) out string databaseName, out string collectionName, out ResourceType resourceType, @@ -575,6 +626,9 @@ public static ResourceType GetResourcePathSegment(string resourcePathSegment) case Paths.InteropUsersPathSegment: return ResourceType.InteropUser; + + case Paths.AzureRbacPathSegment: + return ResourceType.AzureRbac; } string errorMessage = string.Format(CultureInfo.CurrentUICulture, RMResources.UnknownResourceType, resourcePathSegment); @@ -665,6 +719,9 @@ public static string GetResourcePath(ResourceType resourceType) case ResourceType.RetriableWriteCachedResponse: return Paths.RetriableWriteCachedResponsePathSegment; + case ResourceType.AzureRbac: + return Paths.AzureRbacPathSegment; + #if !COSMOSCLIENT case ResourceType.MasterPartition: case ResourceType.ServerPartition: @@ -857,6 +914,7 @@ public static string GeneratePathForNamedBasedInternalResources(ResourceType res ResourceType.InteropUser => Paths.InteropUsersPathSegment + "/" + resourceName, ResourceType.AuthPolicyElement => Paths.AuthPolicyElementsPathSegment + "/" + resourceName, ResourceType.EncryptionScope => Paths.EncryptionScopesPathSegment + "/" + resourceName, + ResourceType.AzureRbac => Paths.AzureRbacPathSegment + "/" + resourceName, _ => null }; } @@ -879,7 +937,8 @@ private static string GeneratePathForNameBased(ResourceType resourceType, string resourceType != ResourceType.RoleDefinition && resourceType != ResourceType.RoleAssignment && resourceType != ResourceType.InteropUser && - resourceType != ResourceType.AuthPolicyElement) + resourceType != ResourceType.AuthPolicyElement && + resourceType != ResourceType.AzureRbac) { string errorMessage = string.Format(CultureInfo.InvariantCulture, RMResources.UnexpectedResourceType, resourceType); throw new BadRequestException(errorMessage); @@ -1011,6 +1070,10 @@ private static string GeneratePathForNameBased(ResourceType resourceType, string { return Paths.AuthPolicyElementsPathSegment; } + else if (resourceType == ResourceType.AzureRbac) + { + return Paths.AzureRbacPathSegment; + } else { string errorMessage = string.Format(CultureInfo.CurrentUICulture, RMResources.UnknownResourceType, resourceType.ToString()); @@ -1041,7 +1104,8 @@ public static string GeneratePath(ResourceType resourceType, string ownerOrResou resourceType != ResourceType.RoleAssignment && resourceType != ResourceType.RoleDefinition && resourceType != ResourceType.InteropUser && - resourceType != ResourceType.AuthPolicyElement + resourceType != ResourceType.AuthPolicyElement && + resourceType != ResourceType.AzureRbac #if !COSMOSCLIENT && resourceType != ResourceType.MasterPartition && resourceType != ResourceType.ServerPartition && @@ -1380,6 +1444,14 @@ public static string GeneratePath(ResourceType resourceType, string ownerOrResou { return Paths.InteropUsersPathSegment + "/" + ownerOrResourceId.ToString(); } + else if (isFeed && resourceType == ResourceType.AzureRbac) + { + return Paths.AzureRbacPathSegment; + } + else if (resourceType == ResourceType.AzureRbac) + { + return Paths.AzureRbacPathSegment + "/" + ownerOrResourceId.ToString(); + } #if !COSMOSCLIENT else if (isFeed && resourceType == ResourceType.MasterPartition) { @@ -1467,6 +1539,8 @@ public static string GenerateRootOperationPath(OperationType operationType) return Paths.OperationsPathSegment + "/" + Paths.Operations_GetConfiguration; case OperationType.GetFederationConfigurations: return Paths.OperationsPathSegment + "/" + Paths.Operations_GetFederationConfigurations; + case OperationType.GetRegionalConfigurations: + return Paths.OperationsPathSegment + "/" + Paths.Operations_GetRegionalConfigurations; case OperationType.GetDatabaseAccountConfigurations: return Paths.OperationsPathSegment + "/" + Paths.Operations_GetDatabaseAccountConfigurations; case OperationType.GetDatabaseAccountArtifactPermissions: @@ -1497,6 +1571,8 @@ public static string GenerateRootOperationPath(OperationType operationType) return Paths.OperationsPathSegment + "/" + Paths.Operations_GetAadGroups; case OperationType.MetadataCheckAccess: return Paths.OperationsPathSegment + "/" + Paths.Operations_MetadataCheckAccess; + case OperationType.GetAzureRbacAccessCheck: + return Paths.OperationsPathSegment + "/" + Paths.Operations_GetAzureRbacAccessCheck; #endif default: @@ -1547,7 +1623,8 @@ private static bool IsResourceType(in StringSegment resourcePathSegment) resourcePathSegment.Equals(Paths.AuthPolicyElementsPathSegment, StringComparison.OrdinalIgnoreCase) || resourcePathSegment.Equals(Paths.SystemDocumentsPathSegment, StringComparison.OrdinalIgnoreCase) || resourcePathSegment.Equals(Paths.EncryptionScopesPathSegment, StringComparison.OrdinalIgnoreCase) || - resourcePathSegment.Equals(Paths.RetriableWriteCachedResponsePathSegment, StringComparison.OrdinalIgnoreCase); + resourcePathSegment.Equals(Paths.RetriableWriteCachedResponsePathSegment, StringComparison.OrdinalIgnoreCase) || + resourcePathSegment.Equals(Paths.AzureRbacPathSegment, StringComparison.OrdinalIgnoreCase); } private static bool IsRootOperation(in StringSegment operationSegment, in StringSegment operationTypeSegment) @@ -1582,6 +1659,7 @@ private static bool IsRootOperation(in StringSegment operationSegment, in String operationTypeSegment.Equals(Paths.ControllerOperations_BatchReportChargesV2, StringComparison.OrdinalIgnoreCase) || operationTypeSegment.Equals(Paths.ControllerOperations_HeartBeatPingWatchdog, StringComparison.OrdinalIgnoreCase) || operationTypeSegment.Equals(Paths.Operations_GetFederationConfigurations, StringComparison.OrdinalIgnoreCase) || + operationTypeSegment.Equals(Paths.Operations_GetRegionalConfigurations, StringComparison.OrdinalIgnoreCase) || operationTypeSegment.Equals(Paths.Operations_GetStorageServiceConfigurations, StringComparison.OrdinalIgnoreCase) || operationTypeSegment.Equals(Paths.Operations_GetConfiguration, StringComparison.OrdinalIgnoreCase) || operationTypeSegment.Equals(Paths.Operations_GetStorageAccountKey, StringComparison.OrdinalIgnoreCase) || @@ -1596,7 +1674,8 @@ private static bool IsRootOperation(in StringSegment operationSegment, in String operationTypeSegment.Equals(Paths.Operations_ReadReplicaFromServerPartition, StringComparison.OrdinalIgnoreCase) || operationTypeSegment.Equals(Paths.Operations_MasterInitiatedProgressCoordination, StringComparison.OrdinalIgnoreCase) || operationTypeSegment.Equals(Paths.Operations_GetAadGroups, StringComparison.OrdinalIgnoreCase) || - operationTypeSegment.Equals(Paths.Operations_MetadataCheckAccess, StringComparison.OrdinalIgnoreCase); + operationTypeSegment.Equals(Paths.Operations_MetadataCheckAccess, StringComparison.OrdinalIgnoreCase)|| + operationTypeSegment.Equals(Paths.Operations_GetAzureRbacAccessCheck, StringComparison.OrdinalIgnoreCase); } private static bool IsTopLevelOperationOperation(in StringSegment replicaSegment, in StringSegment addressSegment) @@ -1731,6 +1810,12 @@ internal static string[] GetResourcePathArray(ResourceType resourceType) return segments.ToArray(); } + if (resourceType == ResourceType.AzureRbac) + { + segments.Add(Paths.AzureRbacPathSegment); + return segments.ToArray(); + } + segments.Add(Paths.DatabasesPathSegment); if (resourceType == ResourceType.Permission || @@ -1891,6 +1976,10 @@ internal static bool ValidateResourceId(ResourceType resourceType, string resour { return PathsHelper.ValidateAuthPolicyElementId(resourceId); } + if (resourceType == ResourceType.AzureRbac) + { + return PathsHelper.ValidateAzureRbacId(resourceId); + } else { Debug.Assert(false, @@ -2026,6 +2115,12 @@ internal static bool ValidateInteropUserId(string resourceIdString) return ResourceId.TryParse(resourceIdString, out resourceId) && resourceId.InteropUser > 0; } + internal static bool ValidateAzureRbacId(string resourceIdString) + { + ResourceId resourceId = null; + return ResourceId.TryParse(resourceIdString, out resourceId) && resourceId.AzureRbac > 0; + } + internal static bool IsPublicResource(Type resourceType) { if (resourceType == typeof(Database) || @@ -2051,7 +2146,6 @@ internal static bool IsPublicResource(Type resourceType) } } - internal static void ParseCollectionSelfLink(string collectionSelfLink, out string databaseId, out string collectionId) { string[] segments = collectionSelfLink.Split(RuntimeConstants.Separators.Url, StringSplitOptions.RemoveEmptyEntries); @@ -2066,5 +2160,46 @@ internal static void ParseCollectionSelfLink(string collectionSelfLink, out stri databaseId = segments[1]; collectionId = segments[3]; } + + internal static bool TryParseOfferResourceLink(string offerResourceLink, out string databaseId, out string collectionId) + { + databaseId = null; + collectionId = null; + + if (string.IsNullOrWhiteSpace(offerResourceLink)) + { + return false; + } + + string[] segments = offerResourceLink.Split(RuntimeConstants.Separators.Url, StringSplitOptions.RemoveEmptyEntries); + + if ((segments.Length != 4 && segments.Length != 2)) + { + return false; + } + + if (!string.Equals(segments[0], Paths.DatabasesPathSegment, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + databaseId = segments[1]; + + if (segments.Length == 2) + { + collectionId = null; + return true; + } + + if (!string.Equals(segments[2], Paths.CollectionsPathSegment, StringComparison.OrdinalIgnoreCase)) + { + databaseId = null; + collectionId = null; + return false; + } + + collectionId = segments[3]; + return true; + } } } diff --git a/Microsoft.Azure.Cosmos/src/direct/PerfCounters.cs b/Microsoft.Azure.Cosmos/src/direct/PerfCounters.cs index c40ffb0c29..438e2a688b 100644 --- a/Microsoft.Azure.Cosmos/src/direct/PerfCounters.cs +++ b/Microsoft.Azure.Cosmos/src/direct/PerfCounters.cs @@ -13,7 +13,6 @@ internal sealed class PerfCounters : IDisposable private readonly string performanceCategoryHelp; -#pragma warning disable IDE0032 private PerformanceCounter frontendRequestsPerSec; private PerformanceCounter frontendActiveRequests; @@ -49,7 +48,6 @@ internal sealed class PerfCounters : IDisposable private PerformanceCounter routingFailures; private PerformanceCounter backendConnectionOpenFailuresDueToSynRetransmitPerSecond; -#pragma warning restore IDE0032 private PerfCounters(string category, string categoryHelp) { diff --git a/Microsoft.Azure.Cosmos/src/direct/PooledTimer.cs b/Microsoft.Azure.Cosmos/src/direct/PooledTimer.cs index 0f0272dd89..2a50378fe6 100644 --- a/Microsoft.Azure.Cosmos/src/direct/PooledTimer.cs +++ b/Microsoft.Azure.Cosmos/src/direct/PooledTimer.cs @@ -23,9 +23,7 @@ internal sealed class PooledTimer /// /// PooledTimer subscribes to the TimerPool to get notified when the timeout has expired /// -#pragma warning disable IDE0044 // Add readonly modifier private TimerPool timerPool; -#pragma warning restore IDE0044 // Add readonly modifier /// /// tcs is set to completed state if the timeout occurs else its set to cancelled state diff --git a/Microsoft.Azure.Cosmos/src/direct/PreviousImageRetentionPolicy.cs b/Microsoft.Azure.Cosmos/src/direct/PreviousImageRetentionPolicy.cs new file mode 100644 index 0000000000..793628769e --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/direct/PreviousImageRetentionPolicy.cs @@ -0,0 +1,113 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Documents +{ + using System; + using Newtonsoft.Json; + + /// + /// Bitwise Enum for Previous Image mode for Collection features like PITR, all versions and deletes change feed, global secondary indexes etc. + /// + [Flags] + internal enum PreviousImageRetentionMode + { + /// + /// Previous image retention is disabled. + /// + Disabled = 0x00, + + /// + /// Previous image retention is enabled for replace operations. + /// + EnabledForReplaceOperations = 0x01, + + /// + /// Previous image retention is enabled for delete operations. + /// + EnabledForDeleteOperations = 0x02, + + /// + /// Previous image retention is enabled for all operations. + /// + EnabledForAllOperations = EnabledForReplaceOperations | EnabledForDeleteOperations + } + + /// + /// Represents the previous image retention policy for a collection in the Azure Cosmos DB service. + /// + /// + internal sealed class PreviousImageRetentionPolicy : JsonSerializable, ICloneable + { + /// + /// Initializes a new instance of the class for the Azure Cosmos DB service. + /// + public PreviousImageRetentionPolicy() + { + } + + /// + /// Gets or sets the PreviousImageRetentionMode (Disabled, EnabledForReplaceOperations, EnabledForDeleteOperation, or EnabledForAllOperations) in the Azure Cosmos DB service. + /// This is an optional property with a default value of null. + /// + /// + /// One of the values of the enumeration, or null if not set. + /// + [JsonProperty(PropertyName = Constants.Properties.Mode, NullValueHandling = NullValueHandling.Ignore)] + public PreviousImageRetentionMode? PreviousImageRetentionMode + { + get + { + return base.GetEnumValue(Constants.Properties.Mode); + } + set + { + if (value.HasValue) + { + base.SetValue(Constants.Properties.Mode, (int)value.Value); + } + else + { + base.SetValue(Constants.Properties.Mode, null); + } + } + } + + /// + /// Determines whether previous image retention is enabled for replace operations. + /// + /// True if previous image retention is enabled for replace operations; otherwise, false. + public bool IsEnabledForReplaceOperation() + { + return this.PreviousImageRetentionMode.HasValue && + (this.PreviousImageRetentionMode.Value & Documents.PreviousImageRetentionMode.EnabledForReplaceOperations) != Documents.PreviousImageRetentionMode.Disabled; + } + + /// + /// Determines whether previous image retention is enabled for delete operations. + /// + /// True if previous image retention is enabled for delete operations; otherwise, false. + public bool IsEnabledForDeleteOperation() + { + return this.PreviousImageRetentionMode.HasValue && + (this.PreviousImageRetentionMode.Value & Documents.PreviousImageRetentionMode.EnabledForDeleteOperations) != Documents.PreviousImageRetentionMode.Disabled; + } + + /// + /// Performs a deep copy of the previous image retention policy. + /// + /// + /// A clone of the previous image retention policy. + /// + public object Clone() + { + PreviousImageRetentionPolicy cloned = new PreviousImageRetentionPolicy() + { + PreviousImageRetentionMode = this.PreviousImageRetentionMode, + }; + + return cloned; + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/direct/Protocol.cs b/Microsoft.Azure.Cosmos/src/direct/Protocol.cs index 6dcb2fb250..b1ea987c4d 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Protocol.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Protocol.cs @@ -3,8 +3,6 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Documents.Client { - using Microsoft.Azure.Cosmos; - /// /// Specifies the protocol to be used by DocumentClient for communicating to the Azure Cosmos DB service. /// diff --git a/Microsoft.Azure.Cosmos/src/direct/ProxyRequest.cs b/Microsoft.Azure.Cosmos/src/direct/ProxyRequest.cs index ac6435f4a1..9ca39a5a3b 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ProxyRequest.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ProxyRequest.cs @@ -29,6 +29,7 @@ static ProxyRequest() RntbdConstants.RequestIdentifiers.StartEpkHash, RntbdConstants.RequestIdentifiers.EndEpkHash, RntbdConstants.RequestIdentifiers.GlobalDatabaseAccountName, + RntbdConstants.RequestIdentifiers.RegionalDatabaseAccountName, RntbdConstants.RequestIdentifiers.DatabaseName, RntbdConstants.RequestIdentifiers.CollectionName, RntbdConstants.RequestIdentifiers.CollectionRid, diff --git a/Microsoft.Azure.Cosmos/src/direct/QuantizerType.cs b/Microsoft.Azure.Cosmos/src/direct/QuantizerType.cs new file mode 100644 index 0000000000..ba3bff89ee --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/direct/QuantizerType.cs @@ -0,0 +1,28 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Documents +{ + using Newtonsoft.Json.Converters; + using Newtonsoft.Json; + using System.Runtime.Serialization; + + /// + /// Defines the target index type of an vector index path specification in the Azure Cosmos DB service. + /// + [JsonConverter(typeof(StringEnumConverter))] + internal enum QuantizerType + { + /// + /// Represents Product Quantizer + /// + [EnumMember(Value = "product")] + Product, + + /// + /// Represents Spherical Quantizer + /// + [EnumMember(Value = "spherical")] + Spherical, + } +} diff --git a/Microsoft.Azure.Cosmos/src/direct/QueryResult.cs b/Microsoft.Azure.Cosmos/src/direct/QueryResult.cs index d1db4a2f06..c5877f3223 100644 --- a/Microsoft.Azure.Cosmos/src/direct/QueryResult.cs +++ b/Microsoft.Azure.Cosmos/src/direct/QueryResult.cs @@ -21,9 +21,7 @@ internal sealed class QueryResult : IDynamicMetaObjectProvider { private readonly JContainer jObject; private readonly string ownerFullName; -#pragma warning disable IDE0044 // Add readonly modifier private JsonSerializer jsonSerializer; -#pragma warning restore IDE0044 // Add readonly modifier public QueryResult(JContainer jObject, string ownerFullName, JsonSerializer jsonSerializer) { diff --git a/Microsoft.Azure.Cosmos/src/direct/QuorumReader.cs b/Microsoft.Azure.Cosmos/src/direct/QuorumReader.cs index 8a1ba01ef7..24e69cde82 100644 --- a/Microsoft.Azure.Cosmos/src/direct/QuorumReader.cs +++ b/Microsoft.Azure.Cosmos/src/direct/QuorumReader.cs @@ -53,7 +53,7 @@ internal sealed class QuorumReader private const int maxNumberOfPrimaryReadRetries = 6; private const int maxNumberOfReadQuorumRetries = 6; private const int delayBetweenReadBarrierCallsInMs = 5; - + private const int maxBarrierRetriesForMultiRegion = 30; private const int barrierRetryIntervalInMsForMultiRegion = 30; @@ -96,8 +96,18 @@ public async Task ReadStrongAsync( shouldRetryOnSecondary = false; using ReadQuorumResult secondaryQuorumReadResult = await this.ReadQuorumAsync(entity, readQuorumValue, includePrimary, readMode); + switch (secondaryQuorumReadResult.QuorumResult) { + + case ReadQuorumResultKind.QuorumThrottled: + { + ReferenceCountedDisposable storeResult = secondaryQuorumReadResult.GetSelectedResponseAndSkipStoreResultDispose(); + // Return the throttled response directly + StoreResponse response = storeResult.Target.ToResponse(entity.RequestContext.RequestChargeTracker); + return response; + } + case ReadQuorumResultKind.QuorumMet: { return secondaryQuorumReadResult.GetResponseAndSkipStoreResultDispose(); @@ -106,28 +116,46 @@ public async Task ReadStrongAsync( case ReadQuorumResultKind.QuorumSelected: { DocumentServiceRequest barrierRequest = await BarrierRequestHelper.CreateAsync( - entity, - this.authorizationTokenProvider, + entity, + this.authorizationTokenProvider, secondaryQuorumReadResult.SelectedLsn, secondaryQuorumReadResult.GlobalCommittedSelectedLsn); - if (await this.WaitForReadBarrierAsync( - barrierRequest, - allowPrimary: true, - readQuorum: readQuorumValue, - readBarrierLsn: secondaryQuorumReadResult.SelectedLsn, - targetGlobalCommittedLSN: secondaryQuorumReadResult.GlobalCommittedSelectedLsn, - readMode: readMode)) + + (bool isSuccess, StoreResponse throttledResponse) = await this.WaitForReadBarrierAsync( + barrierRequest, + allowPrimary: true, + readQuorum: readQuorumValue, + readBarrierLsn: secondaryQuorumReadResult.SelectedLsn, + targetGlobalCommittedLSN: secondaryQuorumReadResult.GlobalCommittedSelectedLsn, + readMode: readMode); + + if (throttledResponse != null) + { + // Handle throttling by delegating to ResourceThrottleRetryPolicy + DefaultTrace.TraceInformation( + "ReadStrongAsync: Throttling occurred during read barrier. Returning throttled response. StatusCode: {0}, SubStatusCode: {1}, PkRangeId :{2}.", + throttledResponse.StatusCode, + throttledResponse.SubStatusCode, + throttledResponse.PartitionKeyRangeId); + + // Return the real 429 response upstream + return throttledResponse; + } + + if (isSuccess) { return secondaryQuorumReadResult.GetResponseAndSkipStoreResultDispose(); } DefaultTrace.TraceWarning( - "QuorumSelected: Could not converge on the LSN {0} GlobalCommittedLSN {3} ReadMode {4} after primary read barrier with read quorum {1} for strong read, Responses: {2}", - secondaryQuorumReadResult.SelectedLsn, + "QuorumSelected: Could not converge on the LSN {0} GlobalCommittedLSN {3} ReadMode {4} after primary read barrier with read quorum {1} for strong read, Responses: {2}, resourceType: {5}, operationType: {6}", + secondaryQuorumReadResult.SelectedLsn, readQuorumValue, secondaryQuorumReadResult, secondaryQuorumReadResult.GlobalCommittedSelectedLsn, - readMode); + readMode, + entity.ResourceType, + entity.OperationType); entity.RequestContext.UpdateQuorumSelectedStoreResponse(secondaryQuorumReadResult.GetSelectedResponseAndSkipStoreResultDispose()); entity.RequestContext.QuorumSelectedLSN = secondaryQuorumReadResult.SelectedLsn; @@ -140,31 +168,48 @@ public async Task ReadStrongAsync( { if (hasPerformedReadFromPrimary) { - DefaultTrace.TraceWarning("QuorumNotSelected: Primary read already attempted. Quorum could not be selected after retrying on secondaries. ReadStoreResponses: {0}", secondaryQuorumReadResult.ToString()); + DefaultTrace.TraceWarning("QuorumNotSelected: Primary read already attempted. Quorum could not be selected after retrying on secondaries. ReadStoreResponses: {0}, resourceType: {1}, operationType: {2}", + secondaryQuorumReadResult.ToString(), + entity.ResourceType, + entity.OperationType); throw new GoneException(RMResources.ReadQuorumNotMet + $", partitionId: {entity.PartitionKeyRangeIdentity}", SubStatusCodes.Server_ReadQuorumNotMet); } - DefaultTrace.TraceWarning("QuorumNotSelected: Quorum could not be selected with read quorum of {0}", readQuorumValue); + DefaultTrace.TraceWarning("QuorumNotSelected: Quorum could not be selected with read quorum of {0}, resourceType: {1}, operationType: {2}", + readQuorumValue, + entity.ResourceType, + entity.OperationType); + using ReadPrimaryResult response = await this.ReadPrimaryAsync(entity, readQuorumValue, false); if (response.IsSuccessful && response.ShouldRetryOnSecondary) { Debug.Assert(false, "QuorumNotSelected: PrimaryResult has both Successful and ShouldRetryOnSecondary flags set"); - DefaultTrace.TraceCritical("PrimaryResult has both Successful and ShouldRetryOnSecondary flags set. ReadQuorumResult StoreResponses: {0}", secondaryQuorumReadResult.ToString()); + DefaultTrace.TraceCritical("PrimaryResult has both Successful and ShouldRetryOnSecondary flags set. ReadQuorumResult StoreResponses: {0}, resourceType: {1}, operationType: {2}", + secondaryQuorumReadResult.ToString(), + entity.ResourceType, + entity.OperationType); } else if (response.IsSuccessful) { - DefaultTrace.TraceInformation("QuorumNotSelected: ReadPrimary successful"); + DefaultTrace.TraceInformation("QuorumNotSelected: ReadPrimary successful, resourceType: {0}, operationType: {1}", + entity.ResourceType, + entity.OperationType); + return response.GetResponseAndSkipStoreResultDispose(); } else if (response.ShouldRetryOnSecondary) { shouldRetryOnSecondary = true; - DefaultTrace.TraceWarning("QuorumNotSelected: ReadPrimary did not succeed. Will retry on secondary. ReadQuorumResult StoreResponses: {0}", secondaryQuorumReadResult.ToString()); + DefaultTrace.TraceWarning("QuorumNotSelected: ReadPrimary did not succeed. Will retry on secondary. ReadQuorumResult StoreResponses: {0}, resourceType: {1}, operationType: {2}", + secondaryQuorumReadResult.ToString(), + entity.ResourceType, + entity.OperationType); + hasPerformedReadFromPrimary = true; // We have failed to select a quorum before - could very well happen again @@ -176,7 +221,10 @@ public async Task ReadStrongAsync( } else { - DefaultTrace.TraceWarning("QuorumNotSelected: Could not get successful response from ReadPrimary"); + DefaultTrace.TraceWarning("QuorumNotSelected: Could not get successful response from ReadPrimary, resourceType: {0}, operationType: {1}", + entity.ResourceType, + entity.OperationType); + throw new GoneException(RMResources.ReadQuorumNotMet, SubStatusCodes.Server_ReadQuorumNotMet); } } @@ -184,12 +232,19 @@ public async Task ReadStrongAsync( break; default: - DefaultTrace.TraceCritical("Unknown ReadQuorum result {0}", secondaryQuorumReadResult.QuorumResult.ToString()); + DefaultTrace.TraceCritical("Unknown ReadQuorum result {0}, resourceType: {1}, operationType: {2}", + secondaryQuorumReadResult.QuorumResult.ToString(), + entity.ResourceType, + entity.OperationType); + throw new InternalServerErrorException(RMResources.InternalServerError); } } while (--readQuorumRetry > 0 && shouldRetryOnSecondary); - DefaultTrace.TraceWarning("Could not complete read quorum with read quorum value of {0}", readQuorumValue); + DefaultTrace.TraceWarning("Could not complete read quorum with read quorum value of {0}, resourceType: {1}, operationType: {2}", + readQuorumValue, + entity.ResourceType, + entity.OperationType); throw new GoneException( string.Format(CultureInfo.CurrentUICulture, @@ -249,11 +304,11 @@ private async Task ReadQuorumAsync( if (entity.RequestContext.QuorumSelectedStoreResponse == null) { using StoreResultList disposableResponseResult = new(await this.storeReader.ReadMultipleReplicaAsync( - entity, + entity, includePrimary: includePrimary, - replicaCountToRead: readQuorum, + replicaCountToRead: readQuorum, requiresValidLsn: true, - useSessionToken: false, + useSessionToken: false, readMode: readMode)); IList> responseResult = disposableResponseResult.Value; @@ -312,7 +367,36 @@ private async Task ReadQuorumAsync( // ReadBarrier required DocumentServiceRequest barrierRequest = await BarrierRequestHelper.CreateAsync(entity, this.authorizationTokenProvider, readLsn, globalCommittedLSN); - if (!await this.WaitForReadBarrierAsync(barrierRequest, false, readQuorum, readLsn, globalCommittedLSN, readMode)) + (bool isSuccess, StoreResponse throttledResponse) = await this.WaitForReadBarrierAsync( + barrierRequest, + false, + readQuorum, + readLsn, + globalCommittedLSN, + readMode); + // Handle the throttled response case first + if (throttledResponse != null) + { + // Create a StoreResult that will throw the correct exception + using (ReferenceCountedDisposable throttledStoreResult = StoreResult.CreateStoreResult( + storeResponse: throttledResponse, + responseException: null, + requiresValidLsn: false, + useLocalLSNBasedHeaders: false, + replicaHealthStatuses: null, + storePhysicalAddress: null)) + { + return new ReadQuorumResult( + entity.RequestContext.RequestChargeTracker, + ReadQuorumResultKind.QuorumThrottled, + readLsn, + globalCommittedLSN, + throttledStoreResult.TryAddReference(), // Pass ownership of the throttled result + responsesForLogging); + } + } + + if (!isSuccess) { return new ReadQuorumResult( entity.RequestContext.RequestChargeTracker, @@ -349,10 +433,27 @@ private async Task ReadPrimaryAsync( // We would have already refreshed address before reaching here. Avoid performing here. entity.RequestContext.ForceRefreshAddressCache = false; using ReferenceCountedDisposable disposableStoreResult = await this.storeReader.ReadPrimaryAsync( - entity, - requiresValidLsn: true, + entity, + requiresValidLsn: true, useSessionToken: useSessionToken); StoreResult storeResult = disposableStoreResult.Target; + + // Local helper to create a throttled ReadPrimaryResult + ReadPrimaryResult CreateReadPrimaryThrottledResult() => + new ReadPrimaryResult( + requestChargeTracker: entity.RequestContext.RequestChargeTracker, + isSuccessful: true, + shouldRetryOnSecondary: false, + response: disposableStoreResult.TryAddReference()); + + // even though the response is throttled it has valid metadata so LSN wont be < 0 hence the below IsValidStatusCodeForExceptionlessRetry && storeResult.LSN < 0 condition wont trigger throttling response so making a check here + if (storeResult.StatusCode == StatusCodes.TooManyRequests) + { + // Let ResourceThrottleRetryPolicy handle 429 + // Instead of throwing an exception, return a result that indicates throttling + return CreateReadPrimaryThrottledResult(); + } + if (!storeResult.IsValid) { ExceptionDispatchInfo.Capture(storeResult.GetException()).Throw(); @@ -363,8 +464,7 @@ private async Task ReadPrimaryAsync( { // Exceptionless failures should be treated similar to exceptions // Validate LSN for cases where there is no exception because the ReadPrimary has requiresValidLsn: true - return new ReadPrimaryResult( - requestChargeTracker: entity.RequestContext.RequestChargeTracker, isSuccessful: true, shouldRetryOnSecondary: false, response: disposableStoreResult.TryAddReference()); + return CreateReadPrimaryThrottledResult(); } if (storeResult.CurrentReplicaSetSize <= 0 || storeResult.LSN < 0 || storeResult.QuorumAckedLSN < 0) @@ -439,8 +539,8 @@ private async Task WaitForPrimaryLsnAsync( // We would have already refreshed address before reaching here. Avoid performing here. barrierRequest.RequestContext.ForceRefreshAddressCache = false; using ReferenceCountedDisposable storeResult = await this.storeReader.ReadPrimaryAsync( - barrierRequest, - requiresValidLsn: true, + barrierRequest, + requiresValidLsn: true, useSessionToken: false); if (!storeResult.Target.IsValid) { @@ -470,7 +570,8 @@ private async Task WaitForPrimaryLsnAsync( return PrimaryReadOutcome.QuorumNotMet; } - private Task WaitForReadBarrierAsync( + + private Task<(bool isSuccess, StoreResponse throttledResponse)> WaitForReadBarrierAsync( DocumentServiceRequest barrierRequest, bool allowPrimary, int readQuorum, @@ -490,7 +591,7 @@ private Task WaitForReadBarrierAsync( // (Env variable 'AZURE_COSMOS_OLD_BARRIER_REQUESTS_HANDLING_ENABLED' allowing to fall back // This old implementation will be removed (and the environment // variable not been used anymore) after some bake time. - private async Task WaitForReadBarrierOldAsync( + private async Task<(bool isSuccess, StoreResponse throttledResponse)> WaitForReadBarrierOldAsync( DocumentServiceRequest barrierRequest, bool allowPrimary, int readQuorum, @@ -502,9 +603,8 @@ private async Task WaitForReadBarrierOldAsync( int readBarrierRetryCountMultiRegion = QuorumReader.maxBarrierRetriesForMultiRegion; long maxGlobalCommittedLsn = 0; -#pragma warning disable SA1108 + while (readBarrierRetryCount-- > 0) // Retry loop -#pragma warning restore SA1108 { barrierRequest.RequestContext.TimeoutHelper.ThrowGoneIfElapsed(); using StoreResultList disposableResponses = new(await this.storeReader.ReadMultipleReplicaAsync( @@ -517,16 +617,47 @@ private async Task WaitForReadBarrierOldAsync( checkMinLSN: false, forceReadAll: true)); IList> responses = disposableResponses.Value; + // Check if all replicas returned 429 + if (responses.Count > 0 && responses.All(response => response.Target.StatusCode == StatusCodes.TooManyRequests)) + { + DefaultTrace.TraceInformation( + "WaitForReadBarrierOldAsync: All replicas returned 429 Too Many Requests. Yielding early to ResourceThrottleRetryPolicy.. StatusCode: {0}, SubStatusCode: {1}, PkRangeId :{2}.", + responses[0].Target.StatusCode, + responses[0].Target.SubStatusCode, + responses[0].Target.PartitionKeyRangeId); + + return (false, responses.First().Target.ToResponse()); // Return the first 429 response + } + + //pivot to primary if any 410/1022 seen + if (BarrierRequestHelper.IsGoneLeaseNotFound(responses)) + { + bool isPrimarySuccess = await this.TryPrimaryOnlyReadBarrierAsync( + barrierRequest, + requiresValidLsn: true, + readBarrierLsn: readBarrierLsn, + targetGlobalCommittedLSN: targetGlobalCommittedLSN, + readQuorum: readQuorum, + readMode: readMode); + + if (isPrimarySuccess) + { + return (true, null); + } + + barrierRequest.RequestContext.ForceRefreshAddressCache = false; + await Task.Delay(QuorumReader.delayBetweenReadBarrierCallsInMs); + continue; + } long maxGlobalCommittedLsnInResponses = responses.Count > 0 ? responses.Max(response => response.Target.GlobalCommittedLSN) : 0; if ((responses.Count(response => response.Target.LSN >= readBarrierLsn) >= readQuorum) && (!(targetGlobalCommittedLSN > 0) || maxGlobalCommittedLsnInResponses >= targetGlobalCommittedLSN)) { - return true; + return (true, null); } - maxGlobalCommittedLsn = maxGlobalCommittedLsn > maxGlobalCommittedLsnInResponses ? - maxGlobalCommittedLsn : maxGlobalCommittedLsnInResponses; + maxGlobalCommittedLsn = Math.Max(maxGlobalCommittedLsn, maxGlobalCommittedLsnInResponses); //only refresh on first barrier call, set to false for subsequent attempts. barrierRequest.RequestContext.ForceRefreshAddressCache = false; @@ -559,15 +690,42 @@ private async Task WaitForReadBarrierOldAsync( forceReadAll: true)); IList> responses = disposableResponses.Value; + // pivot to primary if any 410/1022 seen + if (BarrierRequestHelper.IsGoneLeaseNotFound(responses)) + { + bool isPrimarySuccess = await this.TryPrimaryOnlyReadBarrierAsync( + barrierRequest, + requiresValidLsn: true, + readBarrierLsn: readBarrierLsn, + targetGlobalCommittedLSN: targetGlobalCommittedLSN, + readQuorum: readQuorum, + readMode: readMode); + + if (isPrimarySuccess) + { + return (true, null); + } + + barrierRequest.RequestContext.ForceRefreshAddressCache = false; + if ((QuorumReader.maxBarrierRetriesForMultiRegion - readBarrierRetryCountMultiRegion) > QuorumReader.maxShortBarrierRetriesForMultiRegion) + { + await Task.Delay(QuorumReader.barrierRetryIntervalInMsForMultiRegion); + } + else + { + await Task.Delay(QuorumReader.shortbarrierRetryIntervalInMsForMultiRegion); + } + continue; + } + long maxGlobalCommittedLsnInResponses = responses.Count > 0 ? responses.Max(response => response.Target.GlobalCommittedLSN) : 0; if ((responses.Count(response => response.Target.LSN >= readBarrierLsn) >= readQuorum) && maxGlobalCommittedLsnInResponses >= targetGlobalCommittedLSN) { - return true; + return (true, null); } - maxGlobalCommittedLsn = maxGlobalCommittedLsn > maxGlobalCommittedLsnInResponses ? - maxGlobalCommittedLsn : maxGlobalCommittedLsnInResponses; + maxGlobalCommittedLsn = Math.Max(maxGlobalCommittedLsn, maxGlobalCommittedLsnInResponses); //trace on last retry. if (readBarrierRetryCountMultiRegion == 0) @@ -591,10 +749,10 @@ private async Task WaitForReadBarrierOldAsync( DefaultTrace.TraceInformation("QuorumReader: WaitForReadBarrierAsync - TargetGlobalCommittedLsn: {0}, MaxGlobalCommittedLsn: {1} ReadMode: {2}.", targetGlobalCommittedLSN, maxGlobalCommittedLsn, readMode); - return false; + return (false, null); } - private async Task WaitForReadBarrierNewAsync( + private async Task<(bool isSuccess, StoreResponse throttledResponse)> WaitForReadBarrierNewAsync( DocumentServiceRequest barrierRequest, bool allowPrimary, int readQuorum, @@ -607,10 +765,8 @@ private async Task WaitForReadBarrierNewAsync( long maxGlobalCommittedLsn = 0; bool hasConvergedOnLSN = false; int readBarrierRetryCount = 0; -#pragma warning disable SA1108 - while (readBarrierRetryCount < defaultBarrierRequestDelays.Length && remainingDelay >= TimeSpan.Zero) // Retry loop + while(readBarrierRetryCount < defaultBarrierRequestDelays.Length && remainingDelay >= TimeSpan.Zero) // Retry loop { -#pragma warning restore SA1108 barrierRequest.RequestContext.TimeoutHelper.ThrowGoneIfElapsed(); ValueStopwatch barrierRequestStopWatch = ValueStopwatch.StartNew(); using StoreResultList disposableResponses = new(await this.storeReader.ReadMultipleReplicaAsync( @@ -624,64 +780,97 @@ private async Task WaitForReadBarrierNewAsync( forceReadAll: !hasConvergedOnLSN)); // for GCLSN a single replica is sufficient - and requests should be issued sequentially barrierRequestStopWatch.Stop(); IList> responses = disposableResponses.Value; + // Check if all replicas returned 429 + if (responses.Count > 0 && responses.All(response => response.Target.StatusCode == StatusCodes.TooManyRequests)) + { + DefaultTrace.TraceInformation( + "WaitForReadBarrierNewAsync: All replicas returned 429 Too Many Requests. Yielding early to ResourceThrottleRetryPolicy. StatusCode: {0}, SubStatusCode: {1}, PkRangeId :{2}.", + responses[0].Target.StatusCode, + responses[0].Target.SubStatusCode, + responses[0].Target.PartitionKeyRangeId); + return (false, responses.First().Target.ToResponse()); // Yield early if all replicas return 429 + } + + TimeSpan previousBarrierRequestLatency = barrierRequestStopWatch.Elapsed; - int readBarrierLsnReachedCount = 0; - long maxGlobalCommittedLsnInResponses = 0; - foreach(ReferenceCountedDisposable response in responses) + //pivot to primary if any 410/1022 seen + if (BarrierRequestHelper.IsGoneLeaseNotFound(responses)) { - maxGlobalCommittedLsnInResponses = Math.Max(maxGlobalCommittedLsnInResponses, response.Target.GlobalCommittedLSN); - if (!hasConvergedOnLSN && response.Target.LSN >= readBarrierLsn) + bool isPrimarySuccess = await this.TryPrimaryOnlyReadBarrierAsync( + barrierRequest, + requiresValidLsn: !hasConvergedOnLSN, + readBarrierLsn: readBarrierLsn, + targetGlobalCommittedLSN: targetGlobalCommittedLSN, + readQuorum: readQuorum, + readMode: readMode); + + if (isPrimarySuccess) { - readBarrierLsnReachedCount++; + return (true, null); } - } - if (!hasConvergedOnLSN && readBarrierLsnReachedCount >= readQuorum) - { - hasConvergedOnLSN = true; + barrierRequest.RequestContext.ForceRefreshAddressCache = false; } + else + { + int readBarrierLsnReachedCount = 0; + long maxGlobalCommittedLsnInResponses = 0; + foreach(ReferenceCountedDisposable response in responses) + { + maxGlobalCommittedLsnInResponses = Math.Max(maxGlobalCommittedLsnInResponses, response.Target.GlobalCommittedLSN); + if (!hasConvergedOnLSN && response.Target.LSN >= readBarrierLsn) + { + readBarrierLsnReachedCount++; + } + } + + if (!hasConvergedOnLSN && readBarrierLsnReachedCount >= readQuorum) + { + hasConvergedOnLSN = true; + } if (hasConvergedOnLSN && (targetGlobalCommittedLSN <= 0 || maxGlobalCommittedLsnInResponses >= targetGlobalCommittedLSN)) { - return true; + return (true, null); } - maxGlobalCommittedLsn = Math.Max(maxGlobalCommittedLsn, maxGlobalCommittedLsnInResponses); + maxGlobalCommittedLsn = Math.Max(maxGlobalCommittedLsn, maxGlobalCommittedLsnInResponses); - //only refresh on first barrier call, set to false for subsequent attempts. - barrierRequest.RequestContext.ForceRefreshAddressCache = false; + //only refresh on first barrier call, set to false for subsequent attempts. + barrierRequest.RequestContext.ForceRefreshAddressCache = false; - bool shouldDelay = BarrierRequestHelper.ShouldDelayBetweenHeadRequests( - previousBarrierRequestLatency, - responses, - defaultBarrierRequestDelays[readBarrierRetryCount], - out TimeSpan maxDelay); + bool shouldDelay = BarrierRequestHelper.ShouldDelayBetweenHeadRequests( + previousBarrierRequestLatency, + responses, + defaultBarrierRequestDelays[readBarrierRetryCount], + out TimeSpan maxDelay); - readBarrierRetryCount++; - if (readBarrierRetryCount >= defaultBarrierRequestDelays.Length || remainingDelay <= TimeSpan.Zero) - { - //trace on last retry. - DefaultTrace.TraceInformation( - "QuorumReader: WaitForReadBarrierAsync - Last barrier request. ReadMode: {0}, " + - "HasLSNConverged: {1}, BarrierRequestRetryCount: {2}, Responses: {3}", - readMode, - hasConvergedOnLSN, - readBarrierRetryCount, - string.Join("; ", responses.Select(r => r.Target))); - } - else if (shouldDelay) - { - TimeSpan delay =maxDelay < remainingDelay ? maxDelay : remainingDelay; - await Task.Delay(delay); - remainingDelay -= delay; + readBarrierRetryCount++; + if (readBarrierRetryCount >= defaultBarrierRequestDelays.Length || remainingDelay <= TimeSpan.Zero) + { + //trace on last retry. + DefaultTrace.TraceInformation( + "QuorumReader: WaitForReadBarrierAsync - Last barrier request. ReadMode: {0}, " + + "HasLSNConverged: {1}, BarrierRequestRetryCount: {2}, Responses: {3}", + readMode, + hasConvergedOnLSN, + readBarrierRetryCount, + string.Join("; ", responses.Select(r => r.Target))); + } + else if (shouldDelay) + { + TimeSpan delay =maxDelay < remainingDelay ? maxDelay : remainingDelay; + await Task.Delay(delay); + remainingDelay -= delay; + } } } DefaultTrace.TraceInformation("QuorumReader: WaitForReadBarrierAsync - TargetGlobalCommittedLsn: {0}, MaxGlobalCommittedLsn: {1} ReadMode: {2}, HasLSNConverged:{3}.", targetGlobalCommittedLSN, maxGlobalCommittedLsn, readMode, hasConvergedOnLSN); - return false; + return (false, null); } private bool IsQuorumMet( @@ -736,17 +925,17 @@ private bool IsQuorumMet( selectedResponse = validReadResponses.First(s => s.Target.LSN == maxLsn); } - readLsn = selectedResponse.Target.ItemLSN == -1 ? + readLsn = selectedResponse.Target.ItemLSN == -1 ? maxLsn : Math.Min(selectedResponse.Target.ItemLSN, maxLsn); globalCommittedLSN = checkForGlobalStrong ? readLsn: -1; long maxGlobalCommittedLSN = validReadResponses.Max(res => res.Target.GlobalCommittedLSN); - + // quorum is met if one of the following conditions are satisfied: // 1. readLsn is greater than zero // AND the number of responses that have the same LSN as the selected response is greater than or equal to the read quorum // AND if applicable, the max GlobalCommittedLSN of all responses is greater than or equal to the lsn of the selected response. - + // 2. if the request is a point-read request, // AND there are more than one response in the readResponses // AND the LSN of the returned resource of the selected response is less than or equal to the minimum lsn of the all the responses, @@ -779,12 +968,44 @@ private bool IsQuorumMet( return isQuorumMet; } + /// + /// Primary-only barrier check: if primary is also 410/1022, we bail out by rethrowing the original exception. + /// Adds a single forced-address-refresh retry to handle stale primary mapping in the SDK. + /// + private async Task TryPrimaryOnlyReadBarrierAsync( + DocumentServiceRequest barrierRequest, + bool requiresValidLsn, + long readBarrierLsn, + long targetGlobalCommittedLSN, + int readQuorum, + ReadMode readMode) + { + // Always force refresh before hitting primary to avoid stale primary selection + barrierRequest.RequestContext.ForceRefreshAddressCache = true; + using (ReferenceCountedDisposable primaryResult = await this.storeReader.ReadPrimaryAsync( + barrierRequest, + requiresValidLsn: requiresValidLsn, + useSessionToken: false)) + { + if (!primaryResult.Target.IsValid || BarrierRequestHelper.IsGoneLeaseNotFound(primaryResult.Target)) + { + // Bail out: propagate the error, do not retry further + ExceptionDispatchInfo.Capture(primaryResult.Target.GetException()).Throw(); + } + + bool hasRequiredLsn = readBarrierLsn <= 0 || primaryResult.Target.LSN >= readBarrierLsn; + bool hasRequiredGlobalCommittedLsn = targetGlobalCommittedLSN <= 0 || primaryResult.Target.GlobalCommittedLSN >= targetGlobalCommittedLSN; + return (hasRequiredLsn && hasRequiredGlobalCommittedLsn); + } + } + #region PrivateClasses private enum ReadQuorumResultKind { QuorumMet, QuorumSelected, - QuorumNotSelected + QuorumNotSelected, + QuorumThrottled } private abstract class ReadResult : IDisposable @@ -941,4 +1162,4 @@ public void Dispose() #endregion } - } +} diff --git a/Microsoft.Azure.Cosmos/src/direct/Range.cs b/Microsoft.Azure.Cosmos/src/direct/Range.cs index 1e9fcf0062..c1066ef38c 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Range.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Range.cs @@ -210,5 +210,99 @@ public int Compare(Range left, Range right) } } } + + public class LengthAwareMinComparer : IComparer> + { + private readonly IComparer boundsComparer; + + public static readonly LengthAwareMinComparer Instance = new LengthAwareMinComparer(TComparer); + + public LengthAwareMinComparer(IComparer boundsComparer) + { + this.boundsComparer = boundsComparer; + } + + public int Compare(Range left, Range right) + { + if (left == null || right == null) + { + throw new ArgumentNullException("Range LengthAwareMinComparer comparison: Both left and right arguments must be non-null."); + } + + int result; + if (typeof(T) == typeof(string)) + { + result = Range.CompareHexStrings(left.Min as string, right.Min as string, boundsComparer); + } + else + { + result = this.boundsComparer.Compare(left.Min, right.Min); + } + + if (result != 0 || left.IsMinInclusive == right.IsMinInclusive) + { + return result; + } + + return left.IsMinInclusive ? -1 : 1; + } + } + + public class LengthAwareMaxComparer : IComparer> + { + private readonly IComparer boundsComparer; + + public static readonly LengthAwareMaxComparer Instance = new LengthAwareMaxComparer(TComparer); + + public LengthAwareMaxComparer(IComparer boundsComparer) + { + this.boundsComparer = boundsComparer; + } + + public int Compare(Range left, Range right) + { + if (left == null || right == null) + { + throw new ArgumentNullException("Range LengthAwareMaxComparer comparison: Both left and right arguments must be non-null."); + } + + int result; + if (typeof(T) == typeof(string)) + { + result = Range.CompareHexStrings(left.Max as string, right.Max as string, boundsComparer); + } + else + { + result = this.boundsComparer.Compare(left.Max, right.Max); + } + + if (result != 0 || left.IsMaxInclusive == right.IsMaxInclusive) + { + return result; + } + + return left.IsMaxInclusive ? 1 : -1; + } + } + + private static int CompareHexStrings(string leftString, string rightString, IComparer boundsComparer) + { + if (leftString == null || rightString == null) + { + throw new ArgumentNullException("Range CompareHexStrings comparison: Both left and right arguments must be non-null."); + } + + if (leftString.Length == rightString.Length) + { + return boundsComparer.Compare((T)(object)leftString, (T)(object)rightString); + } + + string leftStr = leftString.TrimEnd('0'); + string rightStr = rightString.TrimEnd('0'); + + return boundsComparer.Compare( + (T)(object)leftStr, + (T)(object)rightStr); + } } } diff --git a/Microsoft.Azure.Cosmos/src/direct/RegionProximityUtil.cs b/Microsoft.Azure.Cosmos/src/direct/RegionProximityUtil.cs index 8549faebd7..c6ddd1780e 100644 --- a/Microsoft.Azure.Cosmos/src/direct/RegionProximityUtil.cs +++ b/Microsoft.Azure.Cosmos/src/direct/RegionProximityUtil.cs @@ -134,6 +134,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 230 }, { LocationNames.GermanyNorth, 254 }, { LocationNames.GermanyWestCentral, 246 }, + { LocationNames.IndiaSouthCentral, 144 }, { LocationNames.IndonesiaCentral, 94 }, { LocationNames.IsraelCentral, 168 }, { LocationNames.IsraelNorthwest, 168 }, @@ -149,11 +150,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 162 }, { LocationNames.NewZealandNorth, 100 }, { LocationNames.NorthCentralUS, 192 }, + { LocationNames.NortheastUS5, 192 }, { LocationNames.NorthEurope, 262 }, { LocationNames.NorwayEast, 272 }, { LocationNames.NorwayWest, 266 }, { LocationNames.PolandCentral, 252 }, { LocationNames.QatarCentral, 168 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 390 }, { LocationNames.SouthAfricaWest, 394 }, { LocationNames.SouthCentralUS, 174 }, @@ -234,6 +238,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 230 }, { LocationNames.GermanyNorth, 256 }, { LocationNames.GermanyWestCentral, 246 }, + { LocationNames.IndiaSouthCentral, 144 }, { LocationNames.IndonesiaCentral, 92 }, { LocationNames.IsraelCentral, 168 }, { LocationNames.IsraelNorthwest, 168 }, @@ -249,11 +254,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 162 }, { LocationNames.NewZealandNorth, 2 }, { LocationNames.NorthCentralUS, 192 }, + { LocationNames.NortheastUS5, 192 }, { LocationNames.NorthEurope, 262 }, { LocationNames.NorwayEast, 272 }, { LocationNames.NorwayWest, 266 }, { LocationNames.PolandCentral, 252 }, { LocationNames.QatarCentral, 168 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 391 }, { LocationNames.SouthAfricaWest, 392 }, { LocationNames.SouthCentralUS, 174 }, @@ -334,6 +342,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 224 }, { LocationNames.GermanyNorth, 250 }, { LocationNames.GermanyWestCentral, 240 }, + { LocationNames.IndiaSouthCentral, 140 }, { LocationNames.IndonesiaCentral, 88 }, { LocationNames.IsraelCentral, 164 }, { LocationNames.IsraelNorthwest, 164 }, @@ -349,11 +358,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 158 }, { LocationNames.NewZealandNorth, 6 }, { LocationNames.NorthCentralUS, 187 }, + { LocationNames.NortheastUS5, 187 }, { LocationNames.NorthEurope, 258 }, { LocationNames.NorwayEast, 266 }, { LocationNames.NorwayWest, 260 }, { LocationNames.PolandCentral, 248 }, { LocationNames.QatarCentral, 164 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 386 }, { LocationNames.SouthAfricaWest, 390 }, { LocationNames.SouthCentralUS, 170 }, @@ -434,6 +446,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 222 }, { LocationNames.GermanyNorth, 246 }, { LocationNames.GermanyWestCentral, 238 }, + { LocationNames.IndiaSouthCentral, 136 }, { LocationNames.IndonesiaCentral, 85 }, { LocationNames.IsraelCentral, 160 }, { LocationNames.IsraelNorthwest, 160 }, @@ -449,11 +462,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 168 }, { LocationNames.NewZealandNorth, 18 }, { LocationNames.NorthCentralUS, 198 }, + { LocationNames.NortheastUS5, 198 }, { LocationNames.NorthEurope, 260 }, { LocationNames.NorwayEast, 264 }, { LocationNames.NorwayWest, 258 }, { LocationNames.PolandCentral, 244 }, { LocationNames.QatarCentral, 160 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 382 }, { LocationNames.SouthAfricaWest, 386 }, { LocationNames.SouthCentralUS, 180 }, @@ -534,6 +550,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 10 }, { LocationNames.GermanyNorth, 16 }, { LocationNames.GermanyWestCentral, 6 }, + { LocationNames.IndiaSouthCentral, 112 }, { LocationNames.IndonesiaCentral, 148 }, { LocationNames.IsraelCentral, 106 }, { LocationNames.IsraelNorthwest, 106 }, @@ -549,11 +566,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 154 }, { LocationNames.NewZealandNorth, 240 }, { LocationNames.NorthCentralUS, 110 }, + { LocationNames.NortheastUS5, 110 }, { LocationNames.NorthEurope, 31 }, { LocationNames.NorwayEast, 32 }, { LocationNames.NorwayWest, 26 }, { LocationNames.PolandCentral, 12 }, { LocationNames.QatarCentral, 106 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 172 }, { LocationNames.SouthAfricaWest, 162 }, { LocationNames.SouthCentralUS, 118 }, @@ -634,6 +654,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 17 }, { LocationNames.GermanyNorth, 10 }, { LocationNames.GermanyWestCentral, 100 }, + { LocationNames.IndiaSouthCentral, 118 }, { LocationNames.IndonesiaCentral, 154 }, { LocationNames.IsraelCentral, 112 }, { LocationNames.IsraelNorthwest, 112 }, @@ -649,11 +670,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 148 }, { LocationNames.NewZealandNorth, 246 }, { LocationNames.NorthCentralUS, 106 }, + { LocationNames.NortheastUS5, 106 }, { LocationNames.NorthEurope, 25 }, { LocationNames.NorwayEast, 26 }, { LocationNames.NorwayWest, 20 }, { LocationNames.PolandCentral, 8 }, { LocationNames.QatarCentral, 112 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 178 }, { LocationNames.SouthAfricaWest, 156 }, { LocationNames.SouthCentralUS, 116 }, @@ -707,7 +731,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.AustriaEast, long.MaxValue }, { LocationNames.BelgiumCentral, long.MaxValue }, { LocationNames.BleuFranceCentral, 0 }, - { LocationNames.BleuFranceSouth, 255 }, + { LocationNames.BleuFranceSouth, 11 }, { LocationNames.BrazilSouth, long.MaxValue }, { LocationNames.BrazilSoutheast, long.MaxValue }, { LocationNames.CanadaCentral, long.MaxValue }, @@ -734,6 +758,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -749,11 +774,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -806,7 +834,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.AustraliaSoutheast, long.MaxValue }, { LocationNames.AustriaEast, long.MaxValue }, { LocationNames.BelgiumCentral, long.MaxValue }, - { LocationNames.BleuFranceCentral, 255 }, + { LocationNames.BleuFranceCentral, 11 }, { LocationNames.BleuFranceSouth, 0 }, { LocationNames.BrazilSouth, long.MaxValue }, { LocationNames.BrazilSoutheast, long.MaxValue }, @@ -834,6 +862,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -849,11 +878,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -934,6 +966,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 200 }, { LocationNames.GermanyNorth, 198 }, { LocationNames.GermanyWestCentral, 192 }, + { LocationNames.IndiaSouthCentral, 302 }, { LocationNames.IndonesiaCentral, 328 }, { LocationNames.IsraelCentral, 296 }, { LocationNames.IsraelNorthwest, 296 }, @@ -949,11 +982,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 182 }, { LocationNames.NewZealandNorth, 314 }, { LocationNames.NorthCentralUS, 138 }, + { LocationNames.NortheastUS5, 138 }, { LocationNames.NorthEurope, 177 }, { LocationNames.NorwayEast, 208 }, { LocationNames.NorwayWest, 201 }, { LocationNames.PolandCentral, 188 }, { LocationNames.QatarCentral, 296 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 342 }, { LocationNames.SouthAfricaWest, 326 }, { LocationNames.SouthCentralUS, 140 }, @@ -1034,6 +1070,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 200 }, { LocationNames.GermanyNorth, 198 }, { LocationNames.GermanyWestCentral, 192 }, + { LocationNames.IndiaSouthCentral, 302 }, { LocationNames.IndonesiaCentral, 328 }, { LocationNames.IsraelCentral, 296 }, { LocationNames.IsraelNorthwest, 296 }, @@ -1049,11 +1086,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 182 }, { LocationNames.NewZealandNorth, 314 }, { LocationNames.NorthCentralUS, 138 }, + { LocationNames.NortheastUS5, 138 }, { LocationNames.NorthEurope, 177 }, { LocationNames.NorwayEast, 208 }, { LocationNames.NorwayWest, 201 }, { LocationNames.PolandCentral, 188 }, { LocationNames.QatarCentral, 296 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 342 }, { LocationNames.SouthAfricaWest, 326 }, { LocationNames.SouthCentralUS, 140 }, @@ -1134,6 +1174,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 106 }, { LocationNames.GermanyNorth, 104 }, { LocationNames.GermanyWestCentral, 98 }, + { LocationNames.IndiaSouthCentral, 208 }, { LocationNames.IndonesiaCentral, 216 }, { LocationNames.IsraelCentral, 201 }, { LocationNames.IsraelNorthwest, 201 }, @@ -1149,11 +1190,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 57 }, { LocationNames.NewZealandNorth, 204 }, { LocationNames.NorthCentralUS, 14 }, + { LocationNames.NortheastUS5, 14 }, { LocationNames.NorthEurope, 84 }, { LocationNames.NorwayEast, 114 }, { LocationNames.NorwayWest, 108 }, { LocationNames.PolandCentral, 94 }, { LocationNames.QatarCentral, 201 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 246 }, { LocationNames.SouthAfricaWest, 230 }, { LocationNames.SouthCentralUS, 42 }, @@ -1234,6 +1278,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 116 }, { LocationNames.GermanyNorth, 114 }, { LocationNames.GermanyWestCentral, 108 }, + { LocationNames.IndiaSouthCentral, 218 }, { LocationNames.IndonesiaCentral, 224 }, { LocationNames.IsraelCentral, 210 }, { LocationNames.IsraelNorthwest, 210 }, @@ -1249,11 +1294,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 66 }, { LocationNames.NewZealandNorth, 214 }, { LocationNames.NorthCentralUS, 24 }, + { LocationNames.NortheastUS5, 24 }, { LocationNames.NorthEurope, 93 }, { LocationNames.NorwayEast, 124 }, { LocationNames.NorwayWest, 116 }, { LocationNames.PolandCentral, 103 }, { LocationNames.QatarCentral, 210 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 256 }, { LocationNames.SouthAfricaWest, 238 }, { LocationNames.SouthCentralUS, 52 }, @@ -1334,6 +1382,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 102 }, { LocationNames.GermanyNorth, 128 }, { LocationNames.GermanyWestCentral, 118 }, + { LocationNames.IndiaSouthCentral, 100 }, { LocationNames.IndonesiaCentral, 52 }, { LocationNames.IsraelCentral, 30 }, { LocationNames.IsraelNorthwest, 30 }, @@ -1349,11 +1398,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 212 }, { LocationNames.NewZealandNorth, 144 }, { LocationNames.NorthCentralUS, 216 }, + { LocationNames.NortheastUS5, 216 }, { LocationNames.NorthEurope, 137 }, { LocationNames.NorwayEast, 146 }, { LocationNames.NorwayWest, 136 }, { LocationNames.PolandCentral, 126 }, { LocationNames.QatarCentral, 30 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 264 }, { LocationNames.SouthAfricaWest, 267 }, { LocationNames.SouthCentralUS, 224 }, @@ -1434,6 +1486,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 110 }, { LocationNames.GermanyNorth, 118 }, { LocationNames.GermanyWestCentral, 112 }, + { LocationNames.IndiaSouthCentral, 222 }, { LocationNames.IndonesiaCentral, 194 }, { LocationNames.IsraelCentral, 216 }, { LocationNames.IsraelNorthwest, 216 }, @@ -1449,11 +1502,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 36 }, { LocationNames.NewZealandNorth, 184 }, { LocationNames.NorthCentralUS, 8 }, + { LocationNames.NortheastUS5, 8 }, { LocationNames.NorthEurope, 92 }, { LocationNames.NorwayEast, 128 }, { LocationNames.NorwayWest, 121 }, { LocationNames.PolandCentral, 102 }, { LocationNames.QatarCentral, 216 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 260 }, { LocationNames.SouthAfricaWest, 244 }, { LocationNames.SouthCentralUS, 22 }, @@ -1534,6 +1590,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 200 }, { LocationNames.GermanyNorth, 198 }, { LocationNames.GermanyWestCentral, 192 }, + { LocationNames.IndiaSouthCentral, 302 }, { LocationNames.IndonesiaCentral, 328 }, { LocationNames.IsraelCentral, 296 }, { LocationNames.IsraelNorthwest, 296 }, @@ -1549,11 +1606,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 182 }, { LocationNames.NewZealandNorth, 314 }, { LocationNames.NorthCentralUS, 138 }, + { LocationNames.NortheastUS5, 138 }, { LocationNames.NorthEurope, 177 }, { LocationNames.NorwayEast, 208 }, { LocationNames.NorwayWest, 201 }, { LocationNames.PolandCentral, 188 }, { LocationNames.QatarCentral, 296 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 342 }, { LocationNames.SouthAfricaWest, 326 }, { LocationNames.SouthCentralUS, 140 }, @@ -1634,6 +1694,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -1649,11 +1710,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -1734,6 +1798,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -1749,11 +1814,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -1834,6 +1902,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -1849,11 +1918,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -1934,6 +2006,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -1949,11 +2022,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -2034,6 +2110,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -2049,11 +2126,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -2134,6 +2214,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -2149,11 +2230,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -2222,7 +2306,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.ChinaNorth2, long.MaxValue }, { LocationNames.ChinaNorth3, long.MaxValue }, { LocationNames.DelosCloudGermanyCentral, 0 }, - { LocationNames.DelosCloudGermanyNorth, 255 }, + { LocationNames.DelosCloudGermanyNorth, 11 }, { LocationNames.DenmarkEast, long.MaxValue }, { LocationNames.EastAsia, long.MaxValue }, { LocationNames.EastUS, long.MaxValue }, @@ -2234,6 +2318,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -2249,11 +2334,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -2321,7 +2409,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.ChinaNorth, long.MaxValue }, { LocationNames.ChinaNorth2, long.MaxValue }, { LocationNames.ChinaNorth3, long.MaxValue }, - { LocationNames.DelosCloudGermanyCentral, 255 }, + { LocationNames.DelosCloudGermanyCentral, 11 }, { LocationNames.DelosCloudGermanyNorth, 0 }, { LocationNames.DenmarkEast, long.MaxValue }, { LocationNames.EastAsia, long.MaxValue }, @@ -2334,6 +2422,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -2349,11 +2438,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -2434,6 +2526,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 20 }, { LocationNames.GermanyNorth, 10 }, { LocationNames.GermanyWestCentral, 8 }, + { LocationNames.IndiaSouthCentral, 126 }, { LocationNames.IndonesiaCentral, 160 }, { LocationNames.IsraelCentral, 118 }, { LocationNames.IsraelNorthwest, 118 }, @@ -2449,11 +2542,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 142 }, { LocationNames.NewZealandNorth, 252 }, { LocationNames.NorthCentralUS, 102 }, + { LocationNames.NortheastUS5, 102 }, { LocationNames.NorthEurope, 22 }, { LocationNames.NorwayEast, 22 }, { LocationNames.NorwayWest, 15 }, { LocationNames.PolandCentral, 100 }, { LocationNames.QatarCentral, 118 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 170 }, { LocationNames.SouthAfricaWest, 152 }, { LocationNames.SouthCentralUS, 112 }, @@ -2534,6 +2630,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 172 }, { LocationNames.GermanyNorth, 196 }, { LocationNames.GermanyWestCentral, 188 }, + { LocationNames.IndiaSouthCentral, 86 }, { LocationNames.IndonesiaCentral, 33 }, { LocationNames.IsraelCentral, 110 }, { LocationNames.IsraelNorthwest, 110 }, @@ -2549,11 +2646,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 140 }, { LocationNames.NewZealandNorth, 120 }, { LocationNames.NorthCentralUS, 184 }, + { LocationNames.NortheastUS5, 184 }, { LocationNames.NorthEurope, 205 }, { LocationNames.NorwayEast, 214 }, { LocationNames.NorwayWest, 206 }, { LocationNames.PolandCentral, 193 }, { LocationNames.QatarCentral, 110 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 332 }, { LocationNames.SouthAfricaWest, 336 }, { LocationNames.SouthCentralUS, 182 }, @@ -2634,6 +2734,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 90 }, { LocationNames.GermanyNorth, 91 }, { LocationNames.GermanyWestCentral, 86 }, + { LocationNames.IndiaSouthCentral, 196 }, { LocationNames.IndonesiaCentral, 218 }, { LocationNames.IsraelCentral, 189 }, { LocationNames.IsraelNorthwest, 189 }, @@ -2649,11 +2750,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 58 }, { LocationNames.NewZealandNorth, 205 }, { LocationNames.NorthCentralUS, 19 }, + { LocationNames.NortheastUS5, 19 }, { LocationNames.NorthEurope, 76 }, { LocationNames.NorwayEast, 101 }, { LocationNames.NorwayWest, 94 }, { LocationNames.PolandCentral, 81 }, { LocationNames.QatarCentral, 189 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 234 }, { LocationNames.SouthAfricaWest, 218 }, { LocationNames.SouthCentralUS, 32 }, @@ -2734,6 +2838,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 90 }, { LocationNames.GermanyNorth, 94 }, { LocationNames.GermanyWestCentral, 90 }, + { LocationNames.IndiaSouthCentral, 198 }, { LocationNames.IndonesiaCentral, 226 }, { LocationNames.IsraelCentral, 190 }, { LocationNames.IsraelNorthwest, 190 }, @@ -2749,11 +2854,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 68 }, { LocationNames.NewZealandNorth, 200 }, { LocationNames.NorthCentralUS, 22 }, + { LocationNames.NortheastUS5, 22 }, { LocationNames.NorthEurope, 75 }, { LocationNames.NorwayEast, 104 }, { LocationNames.NorwayWest, 98 }, { LocationNames.PolandCentral, 86 }, { LocationNames.QatarCentral, 190 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 240 }, { LocationNames.SouthAfricaWest, 222 }, { LocationNames.SouthCentralUS, 26 }, @@ -2834,6 +2942,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 104 }, { LocationNames.GermanyNorth, 112 }, { LocationNames.GermanyWestCentral, 106 }, + { LocationNames.IndiaSouthCentral, 216 }, { LocationNames.IndonesiaCentral, 202 }, { LocationNames.IsraelCentral, 209 }, { LocationNames.IsraelNorthwest, 209 }, @@ -2849,11 +2958,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 44 }, { LocationNames.NewZealandNorth, 192 }, { LocationNames.NorthCentralUS, 100 }, + { LocationNames.NortheastUS5, 100 }, { LocationNames.NorthEurope, 90 }, { LocationNames.NorwayEast, 122 }, { LocationNames.NorwayWest, 114 }, { LocationNames.PolandCentral, 102 }, { LocationNames.QatarCentral, 209 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 253 }, { LocationNames.SouthAfricaWest, 236 }, { LocationNames.SouthCentralUS, 30 }, @@ -2934,6 +3046,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -2949,11 +3062,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -3034,6 +3150,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, 218 }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -3049,11 +3166,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, 19 }, + { LocationNames.NortheastUS5, 19 }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -3134,6 +3254,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 11 }, { LocationNames.GermanyNorth, 20 }, { LocationNames.GermanyWestCentral, 10 }, + { LocationNames.IndiaSouthCentral, 116 }, { LocationNames.IndonesiaCentral, 150 }, { LocationNames.IsraelCentral, 108 }, { LocationNames.IsraelNorthwest, 108 }, @@ -3149,11 +3270,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 140 }, { LocationNames.NewZealandNorth, 242 }, { LocationNames.NorthCentralUS, 96 }, + { LocationNames.NortheastUS5, 96 }, { LocationNames.NorthEurope, 27 }, { LocationNames.NorwayEast, 30 }, { LocationNames.NorwayWest, 24 }, { LocationNames.PolandCentral, 10 }, { LocationNames.QatarCentral, 108 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 166 }, { LocationNames.SouthAfricaWest, 150 }, { LocationNames.SouthCentralUS, 106 }, @@ -3234,6 +3358,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 0 }, { LocationNames.GermanyNorth, 26 }, { LocationNames.GermanyWestCentral, 17 }, + { LocationNames.IndiaSouthCentral, 102 }, { LocationNames.IndonesiaCentral, 138 }, { LocationNames.IsraelCentral, 96 }, { LocationNames.IsraelNorthwest, 96 }, @@ -3249,11 +3374,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 146 }, { LocationNames.NewZealandNorth, 230 }, { LocationNames.NorthCentralUS, 104 }, + { LocationNames.NortheastUS5, 104 }, { LocationNames.NorthEurope, 31 }, { LocationNames.NorwayEast, 42 }, { LocationNames.NorwayWest, 36 }, { LocationNames.PolandCentral, 20 }, { LocationNames.QatarCentral, 96 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 162 }, { LocationNames.SouthAfricaWest, 166 }, { LocationNames.SouthCentralUS, 116 }, @@ -3334,6 +3462,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 26 }, { LocationNames.GermanyNorth, 0 }, { LocationNames.GermanyWestCentral, 10 }, + { LocationNames.IndiaSouthCentral, 128 }, { LocationNames.IndonesiaCentral, 162 }, { LocationNames.IsraelCentral, 120 }, { LocationNames.IsraelNorthwest, 120 }, @@ -3349,11 +3478,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 154 }, { LocationNames.NewZealandNorth, 254 }, { LocationNames.NorthCentralUS, 112 }, + { LocationNames.NortheastUS5, 112 }, { LocationNames.NorthEurope, 31 }, { LocationNames.NorwayEast, 20 }, { LocationNames.NorwayWest, 26 }, { LocationNames.PolandCentral, 10 }, { LocationNames.QatarCentral, 120 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 178 }, { LocationNames.SouthAfricaWest, 162 }, { LocationNames.SouthCentralUS, 120 }, @@ -3434,6 +3566,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 17 }, { LocationNames.GermanyNorth, 10 }, { LocationNames.GermanyWestCentral, 0 }, + { LocationNames.IndiaSouthCentral, 118 }, { LocationNames.IndonesiaCentral, 154 }, { LocationNames.IsraelCentral, 112 }, { LocationNames.IsraelNorthwest, 112 }, @@ -3449,11 +3582,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 148 }, { LocationNames.NewZealandNorth, 246 }, { LocationNames.NorthCentralUS, 106 }, + { LocationNames.NortheastUS5, 106 }, { LocationNames.NorthEurope, 25 }, { LocationNames.NorwayEast, 26 }, { LocationNames.NorwayWest, 20 }, { LocationNames.PolandCentral, 8 }, { LocationNames.QatarCentral, 112 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 178 }, { LocationNames.SouthAfricaWest, 156 }, { LocationNames.SouthCentralUS, 116 }, @@ -3496,6 +3632,110 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.EastUS2EUAP, long.MaxValue }, } }, + { + LocationNames.IndiaSouthCentral, + new Dictionary() + { + { LocationNames.AustraliaCentral, 144 }, + { LocationNames.AustraliaCentral2, 144 }, + { LocationNames.AustraliaEast, 140 }, + { LocationNames.AustraliaSoutheast, 136 }, + { LocationNames.AustriaEast, 112 }, + { LocationNames.BelgiumCentral, 118 }, + { LocationNames.BleuFranceCentral, long.MaxValue }, + { LocationNames.BleuFranceSouth, long.MaxValue }, + { LocationNames.BrazilSouth, 302 }, + { LocationNames.BrazilSoutheast, 302 }, + { LocationNames.CanadaCentral, 208 }, + { LocationNames.CanadaEast, 218 }, + { LocationNames.CentralIndia, 100 }, + { LocationNames.CentralUS, 222 }, + { LocationNames.ChileCentral, 302 }, + { LocationNames.ChinaEast, long.MaxValue }, + { LocationNames.ChinaEast2, long.MaxValue }, + { LocationNames.ChinaEast3, long.MaxValue }, + { LocationNames.ChinaNorth, long.MaxValue }, + { LocationNames.ChinaNorth2, long.MaxValue }, + { LocationNames.ChinaNorth3, long.MaxValue }, + { LocationNames.DelosCloudGermanyCentral, long.MaxValue }, + { LocationNames.DelosCloudGermanyNorth, long.MaxValue }, + { LocationNames.DenmarkEast, 126 }, + { LocationNames.EastAsia, 86 }, + { LocationNames.EastUS, 196 }, + { LocationNames.EastUS2, 198 }, + { LocationNames.EastUS3, 216 }, + { LocationNames.EastUSSLV, long.MaxValue }, + { LocationNames.EastUSSTG, long.MaxValue }, + { LocationNames.FranceCentral, 116 }, + { LocationNames.FranceSouth, 102 }, + { LocationNames.GermanyNorth, 128 }, + { LocationNames.GermanyWestCentral, 118 }, + { LocationNames.IndiaSouthCentral, 0 }, + { LocationNames.IndonesiaCentral, 52 }, + { LocationNames.IsraelCentral, 30 }, + { LocationNames.IsraelNorthwest, 30 }, + { LocationNames.ItalyNorth, 112 }, + { LocationNames.JapanEast, 120 }, + { LocationNames.JapanWest, 124 }, + { LocationNames.JioIndiaCentral, 100 }, + { LocationNames.JioIndiaWest, 100 }, + { LocationNames.KoreaCentral, 114 }, + { LocationNames.KoreaSouth, 106 }, + { LocationNames.MalaysiaSouth, 52 }, + { LocationNames.MalaysiaWest, 52 }, + { LocationNames.MexicoCentral, 212 }, + { LocationNames.NewZealandNorth, 144 }, + { LocationNames.NorthCentralUS, 216 }, + { LocationNames.NortheastUS5, 216 }, + { LocationNames.NorthEurope, 137 }, + { LocationNames.NorwayEast, 146 }, + { LocationNames.NorwayWest, 136 }, + { LocationNames.PolandCentral, 126 }, + { LocationNames.QatarCentral, 30 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, + { LocationNames.SouthAfricaNorth, 264 }, + { LocationNames.SouthAfricaWest, 267 }, + { LocationNames.SouthCentralUS, 224 }, + { LocationNames.SouthCentralUS2, 224 }, + { LocationNames.SouthCentralUSSTG, long.MaxValue }, + { LocationNames.SoutheastAsia, 52 }, + { LocationNames.SoutheastUS, 212 }, + { LocationNames.SoutheastUS3, 212 }, + { LocationNames.SoutheastUS5, 222 }, + { LocationNames.SouthIndia, 22 }, + { LocationNames.SouthwestUS, 218 }, + { LocationNames.SpainCentral, 116 }, + { LocationNames.SwedenCentral, 146 }, + { LocationNames.SwedenSouth, 146 }, + { LocationNames.SwitzerlandNorth, 112 }, + { LocationNames.SwitzerlandWest, 110 }, + { LocationNames.TaiwanNorth, 86 }, + { LocationNames.TaiwanNorthwest, 86 }, + { LocationNames.UAECentral, 30 }, + { LocationNames.UAENorth, 30 }, + { LocationNames.UKSouth, 122 }, + { LocationNames.UKWest, 126 }, + { LocationNames.USDoDCentral, long.MaxValue }, + { LocationNames.USDoDEast, long.MaxValue }, + { LocationNames.USGovArizona, long.MaxValue }, + { LocationNames.USGovTexas, long.MaxValue }, + { LocationNames.USGovVirginia, long.MaxValue }, + { LocationNames.USNatEast, long.MaxValue }, + { LocationNames.USNatWest, long.MaxValue }, + { LocationNames.USSecEast, long.MaxValue }, + { LocationNames.USSecWest, long.MaxValue }, + { LocationNames.USSecWestCentral, long.MaxValue }, + { LocationNames.WestCentralUS, 236 }, + { LocationNames.WestEurope, 126 }, + { LocationNames.WestIndia, 4 }, + { LocationNames.WestUS, 218 }, + { LocationNames.WestUS2, 212 }, + { LocationNames.WestUS3, 212 }, + { LocationNames.CentralUSEUAP, long.MaxValue }, + { LocationNames.EastUS2EUAP, long.MaxValue }, + } + }, { LocationNames.IndonesiaCentral, new Dictionary() @@ -3534,6 +3774,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 138 }, { LocationNames.GermanyNorth, 162 }, { LocationNames.GermanyWestCentral, 154 }, + { LocationNames.IndiaSouthCentral, 52 }, { LocationNames.IndonesiaCentral, 0 }, { LocationNames.IsraelCentral, 76 }, { LocationNames.IsraelNorthwest, 76 }, @@ -3549,11 +3790,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 160 }, { LocationNames.NewZealandNorth, 94 }, { LocationNames.NorthCentralUS, 202 }, + { LocationNames.NortheastUS5, 202 }, { LocationNames.NorthEurope, 172 }, { LocationNames.NorwayEast, 180 }, { LocationNames.NorwayWest, 174 }, { LocationNames.PolandCentral, 160 }, { LocationNames.QatarCentral, 76 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 298 }, { LocationNames.SouthAfricaWest, 302 }, { LocationNames.SouthCentralUS, 200 }, @@ -3634,6 +3878,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 96 }, { LocationNames.GermanyNorth, 120 }, { LocationNames.GermanyWestCentral, 112 }, + { LocationNames.IndiaSouthCentral, 30 }, { LocationNames.IndonesiaCentral, 76 }, { LocationNames.IsraelCentral, 0 }, { LocationNames.IsraelNorthwest, 100 }, @@ -3649,11 +3894,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 236 }, { LocationNames.NewZealandNorth, 168 }, { LocationNames.NorthCentralUS, 209 }, + { LocationNames.NortheastUS5, 209 }, { LocationNames.NorthEurope, 129 }, { LocationNames.NorwayEast, 138 }, { LocationNames.NorwayWest, 130 }, { LocationNames.PolandCentral, 118 }, { LocationNames.QatarCentral, 4 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 256 }, { LocationNames.SouthAfricaWest, 260 }, { LocationNames.SouthCentralUS, 218 }, @@ -3734,6 +3982,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 96 }, { LocationNames.GermanyNorth, 120 }, { LocationNames.GermanyWestCentral, 112 }, + { LocationNames.IndiaSouthCentral, 30 }, { LocationNames.IndonesiaCentral, 76 }, { LocationNames.IsraelCentral, 100 }, { LocationNames.IsraelNorthwest, 0 }, @@ -3749,11 +3998,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 236 }, { LocationNames.NewZealandNorth, 168 }, { LocationNames.NorthCentralUS, 209 }, + { LocationNames.NortheastUS5, 209 }, { LocationNames.NorthEurope, 129 }, { LocationNames.NorwayEast, 138 }, { LocationNames.NorwayWest, 130 }, { LocationNames.PolandCentral, 118 }, { LocationNames.QatarCentral, 4 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 256 }, { LocationNames.SouthAfricaWest, 260 }, { LocationNames.SouthCentralUS, 218 }, @@ -3834,6 +4086,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 10 }, { LocationNames.GermanyNorth, 16 }, { LocationNames.GermanyWestCentral, 6 }, + { LocationNames.IndiaSouthCentral, 112 }, { LocationNames.IndonesiaCentral, 148 }, { LocationNames.IsraelCentral, 106 }, { LocationNames.IsraelNorthwest, 106 }, @@ -3849,11 +4102,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 154 }, { LocationNames.NewZealandNorth, 240 }, { LocationNames.NorthCentralUS, 110 }, + { LocationNames.NortheastUS5, 110 }, { LocationNames.NorthEurope, 31 }, { LocationNames.NorwayEast, 32 }, { LocationNames.NorwayWest, 26 }, { LocationNames.PolandCentral, 12 }, { LocationNames.QatarCentral, 106 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 172 }, { LocationNames.SouthAfricaWest, 162 }, { LocationNames.SouthCentralUS, 118 }, @@ -3934,6 +4190,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 204 }, { LocationNames.GermanyNorth, 230 }, { LocationNames.GermanyWestCentral, 220 }, + { LocationNames.IndiaSouthCentral, 120 }, { LocationNames.IndonesiaCentral, 68 }, { LocationNames.IsraelCentral, 144 }, { LocationNames.IsraelNorthwest, 144 }, @@ -3949,11 +4206,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 112 }, { LocationNames.NewZealandNorth, 125 }, { LocationNames.NorthCentralUS, 148 }, + { LocationNames.NortheastUS5, 148 }, { LocationNames.NorthEurope, 223 }, { LocationNames.NorwayEast, 254 }, { LocationNames.NorwayWest, 248 }, { LocationNames.PolandCentral, 234 }, { LocationNames.QatarCentral, 144 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 366 }, { LocationNames.SouthAfricaWest, 370 }, { LocationNames.SouthCentralUS, 138 }, @@ -4034,6 +4294,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 206 }, { LocationNames.GermanyNorth, 230 }, { LocationNames.GermanyWestCentral, 220 }, + { LocationNames.IndiaSouthCentral, 124 }, { LocationNames.IndonesiaCentral, 74 }, { LocationNames.IsraelCentral, 149 }, { LocationNames.IsraelNorthwest, 149 }, @@ -4049,11 +4310,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 112 }, { LocationNames.NewZealandNorth, 130 }, { LocationNames.NorthCentralUS, 152 }, + { LocationNames.NortheastUS5, 152 }, { LocationNames.NorthEurope, 236 }, { LocationNames.NorwayEast, 248 }, { LocationNames.NorwayWest, 240 }, { LocationNames.PolandCentral, 228 }, { LocationNames.QatarCentral, 149 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 366 }, { LocationNames.SouthAfricaWest, 370 }, { LocationNames.SouthCentralUS, 138 }, @@ -4134,6 +4398,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 102 }, { LocationNames.GermanyNorth, 128 }, { LocationNames.GermanyWestCentral, 118 }, + { LocationNames.IndiaSouthCentral, 100 }, { LocationNames.IndonesiaCentral, 52 }, { LocationNames.IsraelCentral, 30 }, { LocationNames.IsraelNorthwest, 30 }, @@ -4149,11 +4414,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 212 }, { LocationNames.NewZealandNorth, 144 }, { LocationNames.NorthCentralUS, 216 }, + { LocationNames.NortheastUS5, 216 }, { LocationNames.NorthEurope, 137 }, { LocationNames.NorwayEast, 146 }, { LocationNames.NorwayWest, 136 }, { LocationNames.PolandCentral, 126 }, { LocationNames.QatarCentral, 30 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 264 }, { LocationNames.SouthAfricaWest, 267 }, { LocationNames.SouthCentralUS, 224 }, @@ -4234,6 +4502,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 102 }, { LocationNames.GermanyNorth, 128 }, { LocationNames.GermanyWestCentral, 118 }, + { LocationNames.IndiaSouthCentral, 100 }, { LocationNames.IndonesiaCentral, 52 }, { LocationNames.IsraelCentral, 30 }, { LocationNames.IsraelNorthwest, 30 }, @@ -4249,11 +4518,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 212 }, { LocationNames.NewZealandNorth, 144 }, { LocationNames.NorthCentralUS, 216 }, + { LocationNames.NortheastUS5, 216 }, { LocationNames.NorthEurope, 137 }, { LocationNames.NorwayEast, 146 }, { LocationNames.NorwayWest, 136 }, { LocationNames.PolandCentral, 126 }, { LocationNames.QatarCentral, 30 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 264 }, { LocationNames.SouthAfricaWest, 267 }, { LocationNames.SouthCentralUS, 224 }, @@ -4334,6 +4606,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 200 }, { LocationNames.GermanyNorth, 224 }, { LocationNames.GermanyWestCentral, 216 }, + { LocationNames.IndiaSouthCentral, 114 }, { LocationNames.IndonesiaCentral, 62 }, { LocationNames.IsraelCentral, 138 }, { LocationNames.IsraelNorthwest, 138 }, @@ -4349,11 +4622,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 122 }, { LocationNames.NewZealandNorth, 154 }, { LocationNames.NorthCentralUS, 165 }, + { LocationNames.NortheastUS5, 165 }, { LocationNames.NorthEurope, 233 }, { LocationNames.NorwayEast, 242 }, { LocationNames.NorwayWest, 236 }, { LocationNames.PolandCentral, 222 }, { LocationNames.QatarCentral, 138 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 360 }, { LocationNames.SouthAfricaWest, 364 }, { LocationNames.SouthCentralUS, 152 }, @@ -4434,6 +4710,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 193 }, { LocationNames.GermanyNorth, 218 }, { LocationNames.GermanyWestCentral, 209 }, + { LocationNames.IndiaSouthCentral, 106 }, { LocationNames.IndonesiaCentral, 56 }, { LocationNames.IsraelCentral, 132 }, { LocationNames.IsraelNorthwest, 132 }, @@ -4449,11 +4726,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 126 }, { LocationNames.NewZealandNorth, 148 }, { LocationNames.NorthCentralUS, 170 }, + { LocationNames.NortheastUS5, 170 }, { LocationNames.NorthEurope, 226 }, { LocationNames.NorwayEast, 235 }, { LocationNames.NorwayWest, 228 }, { LocationNames.PolandCentral, 216 }, { LocationNames.QatarCentral, 132 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 354 }, { LocationNames.SouthAfricaWest, 358 }, { LocationNames.SouthCentralUS, 156 }, @@ -4534,6 +4814,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 138 }, { LocationNames.GermanyNorth, 162 }, { LocationNames.GermanyWestCentral, 154 }, + { LocationNames.IndiaSouthCentral, 52 }, { LocationNames.IndonesiaCentral, 100 }, { LocationNames.IsraelCentral, 76 }, { LocationNames.IsraelNorthwest, 76 }, @@ -4549,11 +4830,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 160 }, { LocationNames.NewZealandNorth, 94 }, { LocationNames.NorthCentralUS, 202 }, + { LocationNames.NortheastUS5, 202 }, { LocationNames.NorthEurope, 172 }, { LocationNames.NorwayEast, 180 }, { LocationNames.NorwayWest, 174 }, { LocationNames.PolandCentral, 160 }, { LocationNames.QatarCentral, 76 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 298 }, { LocationNames.SouthAfricaWest, 302 }, { LocationNames.SouthCentralUS, 200 }, @@ -4634,6 +4918,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 138 }, { LocationNames.GermanyNorth, 162 }, { LocationNames.GermanyWestCentral, 154 }, + { LocationNames.IndiaSouthCentral, 52 }, { LocationNames.IndonesiaCentral, 100 }, { LocationNames.IsraelCentral, 76 }, { LocationNames.IsraelNorthwest, 76 }, @@ -4649,11 +4934,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 160 }, { LocationNames.NewZealandNorth, 94 }, { LocationNames.NorthCentralUS, 202 }, + { LocationNames.NortheastUS5, 202 }, { LocationNames.NorthEurope, 172 }, { LocationNames.NorwayEast, 180 }, { LocationNames.NorwayWest, 174 }, { LocationNames.PolandCentral, 160 }, { LocationNames.QatarCentral, 76 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 298 }, { LocationNames.SouthAfricaWest, 302 }, { LocationNames.SouthCentralUS, 200 }, @@ -4734,6 +5022,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 146 }, { LocationNames.GermanyNorth, 154 }, { LocationNames.GermanyWestCentral, 148 }, + { LocationNames.IndiaSouthCentral, 212 }, { LocationNames.IndonesiaCentral, 160 }, { LocationNames.IsraelCentral, 236 }, { LocationNames.IsraelNorthwest, 236 }, @@ -4749,11 +5038,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 0 }, { LocationNames.NewZealandNorth, 162 }, { LocationNames.NorthCentralUS, 44 }, + { LocationNames.NortheastUS5, 44 }, { LocationNames.NorthEurope, 136 }, { LocationNames.NorwayEast, 164 }, { LocationNames.NorwayWest, 156 }, { LocationNames.PolandCentral, 142 }, { LocationNames.QatarCentral, 236 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 296 }, { LocationNames.SouthAfricaWest, 280 }, { LocationNames.SouthCentralUS, 44 }, @@ -4834,6 +5126,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 230 }, { LocationNames.GermanyNorth, 254 }, { LocationNames.GermanyWestCentral, 246 }, + { LocationNames.IndiaSouthCentral, 144 }, { LocationNames.IndonesiaCentral, 94 }, { LocationNames.IsraelCentral, 168 }, { LocationNames.IsraelNorthwest, 168 }, @@ -4849,11 +5142,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 162 }, { LocationNames.NewZealandNorth, 0 }, { LocationNames.NorthCentralUS, 192 }, + { LocationNames.NortheastUS5, 192 }, { LocationNames.NorthEurope, 262 }, { LocationNames.NorwayEast, 272 }, { LocationNames.NorwayWest, 266 }, { LocationNames.PolandCentral, 252 }, { LocationNames.QatarCentral, 168 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 390 }, { LocationNames.SouthAfricaWest, 394 }, { LocationNames.SouthCentralUS, 174 }, @@ -4934,6 +5230,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 104 }, { LocationNames.GermanyNorth, 112 }, { LocationNames.GermanyWestCentral, 106 }, + { LocationNames.IndiaSouthCentral, 216 }, { LocationNames.IndonesiaCentral, 202 }, { LocationNames.IsraelCentral, 209 }, { LocationNames.IsraelNorthwest, 209 }, @@ -4949,11 +5246,118 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 44 }, { LocationNames.NewZealandNorth, 192 }, { LocationNames.NorthCentralUS, 0 }, + { LocationNames.NortheastUS5, 100 }, + { LocationNames.NorthEurope, 90 }, + { LocationNames.NorwayEast, 122 }, + { LocationNames.NorwayWest, 114 }, + { LocationNames.PolandCentral, 102 }, + { LocationNames.QatarCentral, 209 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, + { LocationNames.SouthAfricaNorth, 253 }, + { LocationNames.SouthAfricaWest, 236 }, + { LocationNames.SouthCentralUS, 30 }, + { LocationNames.SouthCentralUS2, 30 }, + { LocationNames.SouthCentralUSSTG, 30 }, + { LocationNames.SoutheastAsia, 202 }, + { LocationNames.SoutheastUS, 44 }, + { LocationNames.SoutheastUS3, 44 }, + { LocationNames.SoutheastUS5, 8 }, + { LocationNames.SouthIndia, 228 }, + { LocationNames.SouthwestUS, 50 }, + { LocationNames.SpainCentral, 96 }, + { LocationNames.SwedenCentral, 122 }, + { LocationNames.SwedenSouth, 122 }, + { LocationNames.SwitzerlandNorth, 110 }, + { LocationNames.SwitzerlandWest, 106 }, + { LocationNames.TaiwanNorth, 184 }, + { LocationNames.TaiwanNorthwest, 184 }, + { LocationNames.UAECentral, 209 }, + { LocationNames.UAENorth, 212 }, + { LocationNames.UKSouth, 90 }, + { LocationNames.UKWest, 92 }, + { LocationNames.USDoDCentral, long.MaxValue }, + { LocationNames.USDoDEast, long.MaxValue }, + { LocationNames.USGovArizona, long.MaxValue }, + { LocationNames.USGovTexas, long.MaxValue }, + { LocationNames.USGovVirginia, long.MaxValue }, + { LocationNames.USNatEast, long.MaxValue }, + { LocationNames.USNatWest, long.MaxValue }, + { LocationNames.USSecEast, long.MaxValue }, + { LocationNames.USSecWest, long.MaxValue }, + { LocationNames.USSecWestCentral, long.MaxValue }, + { LocationNames.WestCentralUS, 22 }, + { LocationNames.WestEurope, 102 }, + { LocationNames.WestIndia, 218 }, + { LocationNames.WestUS, 50 }, + { LocationNames.WestUS2, 44 }, + { LocationNames.WestUS3, 44 }, + { LocationNames.CentralUSEUAP, long.MaxValue }, + { LocationNames.EastUS2EUAP, long.MaxValue }, + } + }, + { + LocationNames.NortheastUS5, + new Dictionary() + { + { LocationNames.AustraliaCentral, 192 }, + { LocationNames.AustraliaCentral2, 192 }, + { LocationNames.AustraliaEast, 187 }, + { LocationNames.AustraliaSoutheast, 198 }, + { LocationNames.AustriaEast, 110 }, + { LocationNames.BelgiumCentral, 106 }, + { LocationNames.BleuFranceCentral, long.MaxValue }, + { LocationNames.BleuFranceSouth, long.MaxValue }, + { LocationNames.BrazilSouth, 138 }, + { LocationNames.BrazilSoutheast, 138 }, + { LocationNames.CanadaCentral, 14 }, + { LocationNames.CanadaEast, 24 }, + { LocationNames.CentralIndia, 216 }, + { LocationNames.CentralUS, 8 }, + { LocationNames.ChileCentral, 138 }, + { LocationNames.ChinaEast, long.MaxValue }, + { LocationNames.ChinaEast2, long.MaxValue }, + { LocationNames.ChinaEast3, long.MaxValue }, + { LocationNames.ChinaNorth, long.MaxValue }, + { LocationNames.ChinaNorth2, long.MaxValue }, + { LocationNames.ChinaNorth3, long.MaxValue }, + { LocationNames.DelosCloudGermanyCentral, long.MaxValue }, + { LocationNames.DelosCloudGermanyNorth, long.MaxValue }, + { LocationNames.DenmarkEast, 102 }, + { LocationNames.EastAsia, 184 }, + { LocationNames.EastUS, 19 }, + { LocationNames.EastUS2, 22 }, + { LocationNames.EastUS3, 100 }, + { LocationNames.EastUSSLV, long.MaxValue }, + { LocationNames.EastUSSTG, 19 }, + { LocationNames.FranceCentral, 96 }, + { LocationNames.FranceSouth, 104 }, + { LocationNames.GermanyNorth, 112 }, + { LocationNames.GermanyWestCentral, 106 }, + { LocationNames.IndiaSouthCentral, 216 }, + { LocationNames.IndonesiaCentral, 202 }, + { LocationNames.IsraelCentral, 209 }, + { LocationNames.IsraelNorthwest, 209 }, + { LocationNames.ItalyNorth, 110 }, + { LocationNames.JapanEast, 148 }, + { LocationNames.JapanWest, 152 }, + { LocationNames.JioIndiaCentral, 216 }, + { LocationNames.JioIndiaWest, 216 }, + { LocationNames.KoreaCentral, 165 }, + { LocationNames.KoreaSouth, 170 }, + { LocationNames.MalaysiaSouth, 202 }, + { LocationNames.MalaysiaWest, 202 }, + { LocationNames.MexicoCentral, 44 }, + { LocationNames.NewZealandNorth, 192 }, + { LocationNames.NorthCentralUS, 100 }, + { LocationNames.NortheastUS5, 0 }, { LocationNames.NorthEurope, 90 }, { LocationNames.NorwayEast, 122 }, { LocationNames.NorwayWest, 114 }, { LocationNames.PolandCentral, 102 }, { LocationNames.QatarCentral, 209 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 253 }, { LocationNames.SouthAfricaWest, 236 }, { LocationNames.SouthCentralUS, 30 }, @@ -5034,6 +5438,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 31 }, { LocationNames.GermanyNorth, 31 }, { LocationNames.GermanyWestCentral, 25 }, + { LocationNames.IndiaSouthCentral, 137 }, { LocationNames.IndonesiaCentral, 172 }, { LocationNames.IsraelCentral, 129 }, { LocationNames.IsraelNorthwest, 129 }, @@ -5049,11 +5454,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 136 }, { LocationNames.NewZealandNorth, 262 }, { LocationNames.NorthCentralUS, 90 }, + { LocationNames.NortheastUS5, 90 }, { LocationNames.NorthEurope, 0 }, { LocationNames.NorwayEast, 40 }, { LocationNames.NorwayWest, 33 }, { LocationNames.PolandCentral, 22 }, { LocationNames.QatarCentral, 129 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 179 }, { LocationNames.SouthAfricaWest, 157 }, { LocationNames.SouthCentralUS, 106 }, @@ -5134,6 +5542,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 42 }, { LocationNames.GermanyNorth, 20 }, { LocationNames.GermanyWestCentral, 26 }, + { LocationNames.IndiaSouthCentral, 146 }, { LocationNames.IndonesiaCentral, 180 }, { LocationNames.IsraelCentral, 138 }, { LocationNames.IsraelNorthwest, 138 }, @@ -5149,11 +5558,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 164 }, { LocationNames.NewZealandNorth, 272 }, { LocationNames.NorthCentralUS, 122 }, + { LocationNames.NortheastUS5, 122 }, { LocationNames.NorthEurope, 40 }, { LocationNames.NorwayEast, 0 }, { LocationNames.NorwayWest, 8 }, { LocationNames.PolandCentral, 22 }, { LocationNames.QatarCentral, 138 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 186 }, { LocationNames.SouthAfricaWest, 170 }, { LocationNames.SouthCentralUS, 132 }, @@ -5234,6 +5646,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 36 }, { LocationNames.GermanyNorth, 26 }, { LocationNames.GermanyWestCentral, 20 }, + { LocationNames.IndiaSouthCentral, 136 }, { LocationNames.IndonesiaCentral, 174 }, { LocationNames.IsraelCentral, 130 }, { LocationNames.IsraelNorthwest, 130 }, @@ -5249,11 +5662,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 156 }, { LocationNames.NewZealandNorth, 266 }, { LocationNames.NorthCentralUS, 114 }, + { LocationNames.NortheastUS5, 114 }, { LocationNames.NorthEurope, 33 }, { LocationNames.NorwayEast, 8 }, { LocationNames.NorwayWest, 0 }, { LocationNames.PolandCentral, 15 }, { LocationNames.QatarCentral, 130 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 179 }, { LocationNames.SouthAfricaWest, 162 }, { LocationNames.SouthCentralUS, 124 }, @@ -5334,6 +5750,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 20 }, { LocationNames.GermanyNorth, 10 }, { LocationNames.GermanyWestCentral, 8 }, + { LocationNames.IndiaSouthCentral, 126 }, { LocationNames.IndonesiaCentral, 160 }, { LocationNames.IsraelCentral, 118 }, { LocationNames.IsraelNorthwest, 118 }, @@ -5349,11 +5766,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 142 }, { LocationNames.NewZealandNorth, 252 }, { LocationNames.NorthCentralUS, 102 }, + { LocationNames.NortheastUS5, 102 }, { LocationNames.NorthEurope, 22 }, { LocationNames.NorwayEast, 22 }, { LocationNames.NorwayWest, 15 }, { LocationNames.PolandCentral, 0 }, { LocationNames.QatarCentral, 118 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 170 }, { LocationNames.SouthAfricaWest, 152 }, { LocationNames.SouthCentralUS, 112 }, @@ -5434,6 +5854,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 96 }, { LocationNames.GermanyNorth, 120 }, { LocationNames.GermanyWestCentral, 112 }, + { LocationNames.IndiaSouthCentral, 30 }, { LocationNames.IndonesiaCentral, 76 }, { LocationNames.IsraelCentral, 4 }, { LocationNames.IsraelNorthwest, 4 }, @@ -5449,11 +5870,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 236 }, { LocationNames.NewZealandNorth, 168 }, { LocationNames.NorthCentralUS, 209 }, + { LocationNames.NortheastUS5, 209 }, { LocationNames.NorthEurope, 129 }, { LocationNames.NorwayEast, 138 }, { LocationNames.NorwayWest, 130 }, { LocationNames.PolandCentral, 118 }, { LocationNames.QatarCentral, 0 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 256 }, { LocationNames.SouthAfricaWest, 260 }, { LocationNames.SouthCentralUS, 218 }, @@ -5496,6 +5920,214 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.EastUS2EUAP, long.MaxValue }, } }, + { + LocationNames.SingaporeCentral, + new Dictionary() + { + { LocationNames.AustraliaCentral, long.MaxValue }, + { LocationNames.AustraliaCentral2, long.MaxValue }, + { LocationNames.AustraliaEast, long.MaxValue }, + { LocationNames.AustraliaSoutheast, long.MaxValue }, + { LocationNames.AustriaEast, long.MaxValue }, + { LocationNames.BelgiumCentral, long.MaxValue }, + { LocationNames.BleuFranceCentral, long.MaxValue }, + { LocationNames.BleuFranceSouth, long.MaxValue }, + { LocationNames.BrazilSouth, long.MaxValue }, + { LocationNames.BrazilSoutheast, long.MaxValue }, + { LocationNames.CanadaCentral, long.MaxValue }, + { LocationNames.CanadaEast, long.MaxValue }, + { LocationNames.CentralIndia, long.MaxValue }, + { LocationNames.CentralUS, long.MaxValue }, + { LocationNames.ChileCentral, long.MaxValue }, + { LocationNames.ChinaEast, long.MaxValue }, + { LocationNames.ChinaEast2, long.MaxValue }, + { LocationNames.ChinaEast3, long.MaxValue }, + { LocationNames.ChinaNorth, long.MaxValue }, + { LocationNames.ChinaNorth2, long.MaxValue }, + { LocationNames.ChinaNorth3, long.MaxValue }, + { LocationNames.DelosCloudGermanyCentral, long.MaxValue }, + { LocationNames.DelosCloudGermanyNorth, long.MaxValue }, + { LocationNames.DenmarkEast, long.MaxValue }, + { LocationNames.EastAsia, long.MaxValue }, + { LocationNames.EastUS, long.MaxValue }, + { LocationNames.EastUS2, long.MaxValue }, + { LocationNames.EastUS3, long.MaxValue }, + { LocationNames.EastUSSLV, long.MaxValue }, + { LocationNames.EastUSSTG, long.MaxValue }, + { LocationNames.FranceCentral, long.MaxValue }, + { LocationNames.FranceSouth, long.MaxValue }, + { LocationNames.GermanyNorth, long.MaxValue }, + { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, + { LocationNames.IndonesiaCentral, long.MaxValue }, + { LocationNames.IsraelCentral, long.MaxValue }, + { LocationNames.IsraelNorthwest, long.MaxValue }, + { LocationNames.ItalyNorth, long.MaxValue }, + { LocationNames.JapanEast, long.MaxValue }, + { LocationNames.JapanWest, long.MaxValue }, + { LocationNames.JioIndiaCentral, long.MaxValue }, + { LocationNames.JioIndiaWest, long.MaxValue }, + { LocationNames.KoreaCentral, long.MaxValue }, + { LocationNames.KoreaSouth, long.MaxValue }, + { LocationNames.MalaysiaSouth, long.MaxValue }, + { LocationNames.MalaysiaWest, long.MaxValue }, + { LocationNames.MexicoCentral, long.MaxValue }, + { LocationNames.NewZealandNorth, long.MaxValue }, + { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, + { LocationNames.NorthEurope, long.MaxValue }, + { LocationNames.NorwayEast, long.MaxValue }, + { LocationNames.NorwayWest, long.MaxValue }, + { LocationNames.PolandCentral, long.MaxValue }, + { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, 0 }, + { LocationNames.SingaporeNorth, 11 }, + { LocationNames.SouthAfricaNorth, long.MaxValue }, + { LocationNames.SouthAfricaWest, long.MaxValue }, + { LocationNames.SouthCentralUS, long.MaxValue }, + { LocationNames.SouthCentralUS2, long.MaxValue }, + { LocationNames.SouthCentralUSSTG, long.MaxValue }, + { LocationNames.SoutheastAsia, long.MaxValue }, + { LocationNames.SoutheastUS, long.MaxValue }, + { LocationNames.SoutheastUS3, long.MaxValue }, + { LocationNames.SoutheastUS5, long.MaxValue }, + { LocationNames.SouthIndia, long.MaxValue }, + { LocationNames.SouthwestUS, long.MaxValue }, + { LocationNames.SpainCentral, long.MaxValue }, + { LocationNames.SwedenCentral, long.MaxValue }, + { LocationNames.SwedenSouth, long.MaxValue }, + { LocationNames.SwitzerlandNorth, long.MaxValue }, + { LocationNames.SwitzerlandWest, long.MaxValue }, + { LocationNames.TaiwanNorth, long.MaxValue }, + { LocationNames.TaiwanNorthwest, long.MaxValue }, + { LocationNames.UAECentral, long.MaxValue }, + { LocationNames.UAENorth, long.MaxValue }, + { LocationNames.UKSouth, long.MaxValue }, + { LocationNames.UKWest, long.MaxValue }, + { LocationNames.USDoDCentral, long.MaxValue }, + { LocationNames.USDoDEast, long.MaxValue }, + { LocationNames.USGovArizona, long.MaxValue }, + { LocationNames.USGovTexas, long.MaxValue }, + { LocationNames.USGovVirginia, long.MaxValue }, + { LocationNames.USNatEast, long.MaxValue }, + { LocationNames.USNatWest, long.MaxValue }, + { LocationNames.USSecEast, long.MaxValue }, + { LocationNames.USSecWest, long.MaxValue }, + { LocationNames.USSecWestCentral, long.MaxValue }, + { LocationNames.WestCentralUS, long.MaxValue }, + { LocationNames.WestEurope, long.MaxValue }, + { LocationNames.WestIndia, long.MaxValue }, + { LocationNames.WestUS, long.MaxValue }, + { LocationNames.WestUS2, long.MaxValue }, + { LocationNames.WestUS3, long.MaxValue }, + { LocationNames.CentralUSEUAP, long.MaxValue }, + { LocationNames.EastUS2EUAP, long.MaxValue }, + } + }, + { + LocationNames.SingaporeNorth, + new Dictionary() + { + { LocationNames.AustraliaCentral, long.MaxValue }, + { LocationNames.AustraliaCentral2, long.MaxValue }, + { LocationNames.AustraliaEast, long.MaxValue }, + { LocationNames.AustraliaSoutheast, long.MaxValue }, + { LocationNames.AustriaEast, long.MaxValue }, + { LocationNames.BelgiumCentral, long.MaxValue }, + { LocationNames.BleuFranceCentral, long.MaxValue }, + { LocationNames.BleuFranceSouth, long.MaxValue }, + { LocationNames.BrazilSouth, long.MaxValue }, + { LocationNames.BrazilSoutheast, long.MaxValue }, + { LocationNames.CanadaCentral, long.MaxValue }, + { LocationNames.CanadaEast, long.MaxValue }, + { LocationNames.CentralIndia, long.MaxValue }, + { LocationNames.CentralUS, long.MaxValue }, + { LocationNames.ChileCentral, long.MaxValue }, + { LocationNames.ChinaEast, long.MaxValue }, + { LocationNames.ChinaEast2, long.MaxValue }, + { LocationNames.ChinaEast3, long.MaxValue }, + { LocationNames.ChinaNorth, long.MaxValue }, + { LocationNames.ChinaNorth2, long.MaxValue }, + { LocationNames.ChinaNorth3, long.MaxValue }, + { LocationNames.DelosCloudGermanyCentral, long.MaxValue }, + { LocationNames.DelosCloudGermanyNorth, long.MaxValue }, + { LocationNames.DenmarkEast, long.MaxValue }, + { LocationNames.EastAsia, long.MaxValue }, + { LocationNames.EastUS, long.MaxValue }, + { LocationNames.EastUS2, long.MaxValue }, + { LocationNames.EastUS3, long.MaxValue }, + { LocationNames.EastUSSLV, long.MaxValue }, + { LocationNames.EastUSSTG, long.MaxValue }, + { LocationNames.FranceCentral, long.MaxValue }, + { LocationNames.FranceSouth, long.MaxValue }, + { LocationNames.GermanyNorth, long.MaxValue }, + { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, + { LocationNames.IndonesiaCentral, long.MaxValue }, + { LocationNames.IsraelCentral, long.MaxValue }, + { LocationNames.IsraelNorthwest, long.MaxValue }, + { LocationNames.ItalyNorth, long.MaxValue }, + { LocationNames.JapanEast, long.MaxValue }, + { LocationNames.JapanWest, long.MaxValue }, + { LocationNames.JioIndiaCentral, long.MaxValue }, + { LocationNames.JioIndiaWest, long.MaxValue }, + { LocationNames.KoreaCentral, long.MaxValue }, + { LocationNames.KoreaSouth, long.MaxValue }, + { LocationNames.MalaysiaSouth, long.MaxValue }, + { LocationNames.MalaysiaWest, long.MaxValue }, + { LocationNames.MexicoCentral, long.MaxValue }, + { LocationNames.NewZealandNorth, long.MaxValue }, + { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, + { LocationNames.NorthEurope, long.MaxValue }, + { LocationNames.NorwayEast, long.MaxValue }, + { LocationNames.NorwayWest, long.MaxValue }, + { LocationNames.PolandCentral, long.MaxValue }, + { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, 11 }, + { LocationNames.SingaporeNorth, 0 }, + { LocationNames.SouthAfricaNorth, long.MaxValue }, + { LocationNames.SouthAfricaWest, long.MaxValue }, + { LocationNames.SouthCentralUS, long.MaxValue }, + { LocationNames.SouthCentralUS2, long.MaxValue }, + { LocationNames.SouthCentralUSSTG, long.MaxValue }, + { LocationNames.SoutheastAsia, long.MaxValue }, + { LocationNames.SoutheastUS, long.MaxValue }, + { LocationNames.SoutheastUS3, long.MaxValue }, + { LocationNames.SoutheastUS5, long.MaxValue }, + { LocationNames.SouthIndia, long.MaxValue }, + { LocationNames.SouthwestUS, long.MaxValue }, + { LocationNames.SpainCentral, long.MaxValue }, + { LocationNames.SwedenCentral, long.MaxValue }, + { LocationNames.SwedenSouth, long.MaxValue }, + { LocationNames.SwitzerlandNorth, long.MaxValue }, + { LocationNames.SwitzerlandWest, long.MaxValue }, + { LocationNames.TaiwanNorth, long.MaxValue }, + { LocationNames.TaiwanNorthwest, long.MaxValue }, + { LocationNames.UAECentral, long.MaxValue }, + { LocationNames.UAENorth, long.MaxValue }, + { LocationNames.UKSouth, long.MaxValue }, + { LocationNames.UKWest, long.MaxValue }, + { LocationNames.USDoDCentral, long.MaxValue }, + { LocationNames.USDoDEast, long.MaxValue }, + { LocationNames.USGovArizona, long.MaxValue }, + { LocationNames.USGovTexas, long.MaxValue }, + { LocationNames.USGovVirginia, long.MaxValue }, + { LocationNames.USNatEast, long.MaxValue }, + { LocationNames.USNatWest, long.MaxValue }, + { LocationNames.USSecEast, long.MaxValue }, + { LocationNames.USSecWest, long.MaxValue }, + { LocationNames.USSecWestCentral, long.MaxValue }, + { LocationNames.WestCentralUS, long.MaxValue }, + { LocationNames.WestEurope, long.MaxValue }, + { LocationNames.WestIndia, long.MaxValue }, + { LocationNames.WestUS, long.MaxValue }, + { LocationNames.WestUS2, long.MaxValue }, + { LocationNames.WestUS3, long.MaxValue }, + { LocationNames.CentralUSEUAP, long.MaxValue }, + { LocationNames.EastUS2EUAP, long.MaxValue }, + } + }, { LocationNames.SouthAfricaNorth, new Dictionary() @@ -5534,6 +6166,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 162 }, { LocationNames.GermanyNorth, 178 }, { LocationNames.GermanyWestCentral, 178 }, + { LocationNames.IndiaSouthCentral, 264 }, { LocationNames.IndonesiaCentral, 298 }, { LocationNames.IsraelCentral, 256 }, { LocationNames.IsraelNorthwest, 256 }, @@ -5549,11 +6182,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 296 }, { LocationNames.NewZealandNorth, 390 }, { LocationNames.NorthCentralUS, 253 }, + { LocationNames.NortheastUS5, 253 }, { LocationNames.NorthEurope, 179 }, { LocationNames.NorwayEast, 186 }, { LocationNames.NorwayWest, 179 }, { LocationNames.PolandCentral, 170 }, { LocationNames.QatarCentral, 256 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 0 }, { LocationNames.SouthAfricaWest, 18 }, { LocationNames.SouthCentralUS, 264 }, @@ -5634,6 +6270,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 166 }, { LocationNames.GermanyNorth, 162 }, { LocationNames.GermanyWestCentral, 156 }, + { LocationNames.IndiaSouthCentral, 267 }, { LocationNames.IndonesiaCentral, 302 }, { LocationNames.IsraelCentral, 260 }, { LocationNames.IsraelNorthwest, 260 }, @@ -5649,11 +6286,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 280 }, { LocationNames.NewZealandNorth, 394 }, { LocationNames.NorthCentralUS, 236 }, + { LocationNames.NortheastUS5, 236 }, { LocationNames.NorthEurope, 157 }, { LocationNames.NorwayEast, 170 }, { LocationNames.NorwayWest, 162 }, { LocationNames.PolandCentral, 152 }, { LocationNames.QatarCentral, 260 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 18 }, { LocationNames.SouthAfricaWest, 0 }, { LocationNames.SouthCentralUS, 248 }, @@ -5734,6 +6374,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 116 }, { LocationNames.GermanyNorth, 120 }, { LocationNames.GermanyWestCentral, 116 }, + { LocationNames.IndiaSouthCentral, 224 }, { LocationNames.IndonesiaCentral, 200 }, { LocationNames.IsraelCentral, 218 }, { LocationNames.IsraelNorthwest, 218 }, @@ -5749,11 +6390,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 44 }, { LocationNames.NewZealandNorth, 174 }, { LocationNames.NorthCentralUS, 30 }, + { LocationNames.NortheastUS5, 30 }, { LocationNames.NorthEurope, 106 }, { LocationNames.NorwayEast, 132 }, { LocationNames.NorwayWest, 124 }, { LocationNames.PolandCentral, 112 }, { LocationNames.QatarCentral, 218 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 264 }, { LocationNames.SouthAfricaWest, 248 }, { LocationNames.SouthCentralUS, 0 }, @@ -5834,6 +6478,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 116 }, { LocationNames.GermanyNorth, 120 }, { LocationNames.GermanyWestCentral, 116 }, + { LocationNames.IndiaSouthCentral, 224 }, { LocationNames.IndonesiaCentral, 200 }, { LocationNames.IsraelCentral, 218 }, { LocationNames.IsraelNorthwest, 218 }, @@ -5849,11 +6494,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 44 }, { LocationNames.NewZealandNorth, 174 }, { LocationNames.NorthCentralUS, 30 }, + { LocationNames.NortheastUS5, 30 }, { LocationNames.NorthEurope, 106 }, { LocationNames.NorwayEast, 132 }, { LocationNames.NorwayWest, 124 }, { LocationNames.PolandCentral, 112 }, { LocationNames.QatarCentral, 218 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 264 }, { LocationNames.SouthAfricaWest, 248 }, { LocationNames.SouthCentralUS, 100 }, @@ -5934,6 +6582,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, 200 }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -5949,11 +6598,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, 30 }, + { LocationNames.NortheastUS5, 30 }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -6034,6 +6686,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 138 }, { LocationNames.GermanyNorth, 162 }, { LocationNames.GermanyWestCentral, 154 }, + { LocationNames.IndiaSouthCentral, 52 }, { LocationNames.IndonesiaCentral, 100 }, { LocationNames.IsraelCentral, 76 }, { LocationNames.IsraelNorthwest, 76 }, @@ -6049,11 +6702,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 160 }, { LocationNames.NewZealandNorth, 94 }, { LocationNames.NorthCentralUS, 202 }, + { LocationNames.NortheastUS5, 202 }, { LocationNames.NorthEurope, 172 }, { LocationNames.NorwayEast, 180 }, { LocationNames.NorwayWest, 174 }, { LocationNames.PolandCentral, 160 }, { LocationNames.QatarCentral, 76 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 298 }, { LocationNames.SouthAfricaWest, 302 }, { LocationNames.SouthCentralUS, 200 }, @@ -6134,6 +6790,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 146 }, { LocationNames.GermanyNorth, 154 }, { LocationNames.GermanyWestCentral, 148 }, + { LocationNames.IndiaSouthCentral, 212 }, { LocationNames.IndonesiaCentral, 160 }, { LocationNames.IsraelCentral, 236 }, { LocationNames.IsraelNorthwest, 236 }, @@ -6149,11 +6806,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 100 }, { LocationNames.NewZealandNorth, 162 }, { LocationNames.NorthCentralUS, 44 }, + { LocationNames.NortheastUS5, 44 }, { LocationNames.NorthEurope, 136 }, { LocationNames.NorwayEast, 164 }, { LocationNames.NorwayWest, 156 }, { LocationNames.PolandCentral, 142 }, { LocationNames.QatarCentral, 236 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 296 }, { LocationNames.SouthAfricaWest, 280 }, { LocationNames.SouthCentralUS, 44 }, @@ -6234,6 +6894,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 146 }, { LocationNames.GermanyNorth, 154 }, { LocationNames.GermanyWestCentral, 148 }, + { LocationNames.IndiaSouthCentral, 212 }, { LocationNames.IndonesiaCentral, 160 }, { LocationNames.IsraelCentral, 236 }, { LocationNames.IsraelNorthwest, 236 }, @@ -6249,11 +6910,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 100 }, { LocationNames.NewZealandNorth, 162 }, { LocationNames.NorthCentralUS, 44 }, + { LocationNames.NortheastUS5, 44 }, { LocationNames.NorthEurope, 136 }, { LocationNames.NorwayEast, 164 }, { LocationNames.NorwayWest, 156 }, { LocationNames.PolandCentral, 142 }, { LocationNames.QatarCentral, 236 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 296 }, { LocationNames.SouthAfricaWest, 280 }, { LocationNames.SouthCentralUS, 44 }, @@ -6334,6 +6998,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 110 }, { LocationNames.GermanyNorth, 118 }, { LocationNames.GermanyWestCentral, 112 }, + { LocationNames.IndiaSouthCentral, 222 }, { LocationNames.IndonesiaCentral, 194 }, { LocationNames.IsraelCentral, 216 }, { LocationNames.IsraelNorthwest, 216 }, @@ -6349,11 +7014,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 36 }, { LocationNames.NewZealandNorth, 184 }, { LocationNames.NorthCentralUS, 8 }, + { LocationNames.NortheastUS5, 8 }, { LocationNames.NorthEurope, 92 }, { LocationNames.NorwayEast, 128 }, { LocationNames.NorwayWest, 121 }, { LocationNames.PolandCentral, 102 }, { LocationNames.QatarCentral, 216 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 260 }, { LocationNames.SouthAfricaWest, 244 }, { LocationNames.SouthCentralUS, 22 }, @@ -6434,6 +7102,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 114 }, { LocationNames.GermanyNorth, 140 }, { LocationNames.GermanyWestCentral, 130 }, + { LocationNames.IndiaSouthCentral, 22 }, { LocationNames.IndonesiaCentral, 34 }, { LocationNames.IsraelCentral, 46 }, { LocationNames.IsraelNorthwest, 46 }, @@ -6449,11 +7118,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 192 }, { LocationNames.NewZealandNorth, 126 }, { LocationNames.NorthCentralUS, 228 }, + { LocationNames.NortheastUS5, 228 }, { LocationNames.NorthEurope, 149 }, { LocationNames.NorwayEast, 157 }, { LocationNames.NorwayWest, 150 }, { LocationNames.PolandCentral, 136 }, { LocationNames.QatarCentral, 46 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 276 }, { LocationNames.SouthAfricaWest, 280 }, { LocationNames.SouthCentralUS, 222 }, @@ -6534,6 +7206,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 148 }, { LocationNames.GermanyNorth, 153 }, { LocationNames.GermanyWestCentral, 148 }, + { LocationNames.IndiaSouthCentral, 218 }, { LocationNames.IndonesiaCentral, 168 }, { LocationNames.IsraelCentral, 244 }, { LocationNames.IsraelNorthwest, 244 }, @@ -6549,11 +7222,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 22 }, { LocationNames.NewZealandNorth, 142 }, { LocationNames.NorthCentralUS, 50 }, + { LocationNames.NortheastUS5, 50 }, { LocationNames.NorthEurope, 133 }, { LocationNames.NorwayEast, 163 }, { LocationNames.NorwayWest, 156 }, { LocationNames.PolandCentral, 144 }, { LocationNames.QatarCentral, 244 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 298 }, { LocationNames.SouthAfricaWest, 280 }, { LocationNames.SouthCentralUS, 34 }, @@ -6634,6 +7310,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 11 }, { LocationNames.GermanyNorth, 20 }, { LocationNames.GermanyWestCentral, 10 }, + { LocationNames.IndiaSouthCentral, 116 }, { LocationNames.IndonesiaCentral, 150 }, { LocationNames.IsraelCentral, 108 }, { LocationNames.IsraelNorthwest, 108 }, @@ -6649,11 +7326,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 140 }, { LocationNames.NewZealandNorth, 242 }, { LocationNames.NorthCentralUS, 96 }, + { LocationNames.NortheastUS5, 96 }, { LocationNames.NorthEurope, 27 }, { LocationNames.NorwayEast, 30 }, { LocationNames.NorwayWest, 24 }, { LocationNames.PolandCentral, 10 }, { LocationNames.QatarCentral, 108 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 166 }, { LocationNames.SouthAfricaWest, 150 }, { LocationNames.SouthCentralUS, 106 }, @@ -6734,6 +7414,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 42 }, { LocationNames.GermanyNorth, 20 }, { LocationNames.GermanyWestCentral, 26 }, + { LocationNames.IndiaSouthCentral, 146 }, { LocationNames.IndonesiaCentral, 180 }, { LocationNames.IsraelCentral, 129 }, { LocationNames.IsraelNorthwest, 129 }, @@ -6749,11 +7430,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 164 }, { LocationNames.NewZealandNorth, 272 }, { LocationNames.NorthCentralUS, 122 }, + { LocationNames.NortheastUS5, 122 }, { LocationNames.NorthEurope, 40 }, { LocationNames.NorwayEast, 10 }, { LocationNames.NorwayWest, 10 }, { LocationNames.PolandCentral, 22 }, { LocationNames.QatarCentral, 138 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 186 }, { LocationNames.SouthAfricaWest, 170 }, { LocationNames.SouthCentralUS, 132 }, @@ -6834,6 +7518,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 42 }, { LocationNames.GermanyNorth, 20 }, { LocationNames.GermanyWestCentral, 26 }, + { LocationNames.IndiaSouthCentral, 146 }, { LocationNames.IndonesiaCentral, 180 }, { LocationNames.IsraelCentral, 129 }, { LocationNames.IsraelNorthwest, 129 }, @@ -6849,11 +7534,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 164 }, { LocationNames.NewZealandNorth, 272 }, { LocationNames.NorthCentralUS, 122 }, + { LocationNames.NortheastUS5, 122 }, { LocationNames.NorthEurope, 40 }, { LocationNames.NorwayEast, 10 }, { LocationNames.NorwayWest, 10 }, { LocationNames.PolandCentral, 22 }, { LocationNames.QatarCentral, 138 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 186 }, { LocationNames.SouthAfricaWest, 170 }, { LocationNames.SouthCentralUS, 132 }, @@ -6934,6 +7622,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 10 }, { LocationNames.GermanyNorth, 16 }, { LocationNames.GermanyWestCentral, 6 }, + { LocationNames.IndiaSouthCentral, 112 }, { LocationNames.IndonesiaCentral, 148 }, { LocationNames.IsraelCentral, 106 }, { LocationNames.IsraelNorthwest, 106 }, @@ -6949,11 +7638,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 154 }, { LocationNames.NewZealandNorth, 240 }, { LocationNames.NorthCentralUS, 110 }, + { LocationNames.NortheastUS5, 110 }, { LocationNames.NorthEurope, 31 }, { LocationNames.NorwayEast, 32 }, { LocationNames.NorwayWest, 26 }, { LocationNames.PolandCentral, 12 }, { LocationNames.QatarCentral, 106 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 172 }, { LocationNames.SouthAfricaWest, 162 }, { LocationNames.SouthCentralUS, 118 }, @@ -7034,6 +7726,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 8 }, { LocationNames.GermanyNorth, 20 }, { LocationNames.GermanyWestCentral, 10 }, + { LocationNames.IndiaSouthCentral, 110 }, { LocationNames.IndonesiaCentral, 144 }, { LocationNames.IsraelCentral, 102 }, { LocationNames.IsraelNorthwest, 102 }, @@ -7049,11 +7742,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 146 }, { LocationNames.NewZealandNorth, 236 }, { LocationNames.NorthCentralUS, 106 }, + { LocationNames.NortheastUS5, 106 }, { LocationNames.NorthEurope, 30 }, { LocationNames.NorwayEast, 36 }, { LocationNames.NorwayWest, 30 }, { LocationNames.PolandCentral, 16 }, { LocationNames.QatarCentral, 102 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 168 }, { LocationNames.SouthAfricaWest, 158 }, { LocationNames.SouthCentralUS, 116 }, @@ -7134,6 +7830,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 172 }, { LocationNames.GermanyNorth, 196 }, { LocationNames.GermanyWestCentral, 188 }, + { LocationNames.IndiaSouthCentral, 86 }, { LocationNames.IndonesiaCentral, 33 }, { LocationNames.IsraelCentral, 110 }, { LocationNames.IsraelNorthwest, 110 }, @@ -7149,11 +7846,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 140 }, { LocationNames.NewZealandNorth, 120 }, { LocationNames.NorthCentralUS, 184 }, + { LocationNames.NortheastUS5, 184 }, { LocationNames.NorthEurope, 205 }, { LocationNames.NorwayEast, 214 }, { LocationNames.NorwayWest, 206 }, { LocationNames.PolandCentral, 193 }, { LocationNames.QatarCentral, 110 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 332 }, { LocationNames.SouthAfricaWest, 336 }, { LocationNames.SouthCentralUS, 182 }, @@ -7234,6 +7934,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 172 }, { LocationNames.GermanyNorth, 196 }, { LocationNames.GermanyWestCentral, 188 }, + { LocationNames.IndiaSouthCentral, 86 }, { LocationNames.IndonesiaCentral, 33 }, { LocationNames.IsraelCentral, 110 }, { LocationNames.IsraelNorthwest, 110 }, @@ -7249,11 +7950,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 140 }, { LocationNames.NewZealandNorth, 120 }, { LocationNames.NorthCentralUS, 184 }, + { LocationNames.NortheastUS5, 184 }, { LocationNames.NorthEurope, 205 }, { LocationNames.NorwayEast, 214 }, { LocationNames.NorwayWest, 206 }, { LocationNames.PolandCentral, 193 }, { LocationNames.QatarCentral, 110 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 332 }, { LocationNames.SouthAfricaWest, 336 }, { LocationNames.SouthCentralUS, 182 }, @@ -7334,6 +8038,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 96 }, { LocationNames.GermanyNorth, 120 }, { LocationNames.GermanyWestCentral, 112 }, + { LocationNames.IndiaSouthCentral, 30 }, { LocationNames.IndonesiaCentral, 76 }, { LocationNames.IsraelCentral, 100 }, { LocationNames.IsraelNorthwest, 100 }, @@ -7349,11 +8054,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 236 }, { LocationNames.NewZealandNorth, 168 }, { LocationNames.NorthCentralUS, 209 }, + { LocationNames.NortheastUS5, 209 }, { LocationNames.NorthEurope, 129 }, { LocationNames.NorwayEast, 138 }, { LocationNames.NorwayWest, 130 }, { LocationNames.PolandCentral, 118 }, { LocationNames.QatarCentral, 4 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 256 }, { LocationNames.SouthAfricaWest, 260 }, { LocationNames.SouthCentralUS, 218 }, @@ -7434,6 +8142,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 98 }, { LocationNames.GermanyNorth, 124 }, { LocationNames.GermanyWestCentral, 114 }, + { LocationNames.IndiaSouthCentral, 30 }, { LocationNames.IndonesiaCentral, 76 }, { LocationNames.IsraelCentral, 4 }, { LocationNames.IsraelNorthwest, 4 }, @@ -7449,11 +8158,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 236 }, { LocationNames.NewZealandNorth, 168 }, { LocationNames.NorthCentralUS, 212 }, + { LocationNames.NortheastUS5, 212 }, { LocationNames.NorthEurope, 132 }, { LocationNames.NorwayEast, 140 }, { LocationNames.NorwayWest, 134 }, { LocationNames.PolandCentral, 120 }, { LocationNames.QatarCentral, 4 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 260 }, { LocationNames.SouthAfricaWest, 264 }, { LocationNames.SouthCentralUS, 220 }, @@ -7534,6 +8246,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 16 }, { LocationNames.GermanyNorth, 20 }, { LocationNames.GermanyWestCentral, 14 }, + { LocationNames.IndiaSouthCentral, 122 }, { LocationNames.IndonesiaCentral, 158 }, { LocationNames.IsraelCentral, 116 }, { LocationNames.IsraelNorthwest, 116 }, @@ -7549,11 +8262,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 130 }, { LocationNames.NewZealandNorth, 250 }, { LocationNames.NorthCentralUS, 90 }, + { LocationNames.NortheastUS5, 90 }, { LocationNames.NorthEurope, 21 }, { LocationNames.NorwayEast, 28 }, { LocationNames.NorwayWest, 22 }, { LocationNames.PolandCentral, 10 }, { LocationNames.QatarCentral, 116 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 160 }, { LocationNames.SouthAfricaWest, 144 }, { LocationNames.SouthCentralUS, 104 }, @@ -7634,6 +8350,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 18 }, { LocationNames.GermanyNorth, 22 }, { LocationNames.GermanyWestCentral, 18 }, + { LocationNames.IndiaSouthCentral, 126 }, { LocationNames.IndonesiaCentral, 161 }, { LocationNames.IsraelCentral, 118 }, { LocationNames.IsraelNorthwest, 118 }, @@ -7649,11 +8366,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 132 }, { LocationNames.NewZealandNorth, 252 }, { LocationNames.NorthCentralUS, 92 }, + { LocationNames.NortheastUS5, 92 }, { LocationNames.NorthEurope, 17 }, { LocationNames.NorwayEast, 30 }, { LocationNames.NorwayWest, 24 }, { LocationNames.PolandCentral, 12 }, { LocationNames.QatarCentral, 118 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 164 }, { LocationNames.SouthAfricaWest, 146 }, { LocationNames.SouthCentralUS, 106 }, @@ -7734,6 +8454,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -7749,11 +8470,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -7834,6 +8558,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -7849,11 +8574,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -7934,6 +8662,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -7949,11 +8678,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -8034,6 +8766,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -8049,11 +8782,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -8134,6 +8870,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -8149,11 +8886,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -8234,6 +8974,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -8249,11 +8990,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -8334,6 +9078,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -8349,11 +9094,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -8434,6 +9182,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -8449,11 +9198,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -8534,6 +9286,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -8549,11 +9302,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -8634,6 +9390,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -8649,11 +9406,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -8734,6 +9494,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 126 }, { LocationNames.GermanyNorth, 132 }, { LocationNames.GermanyWestCentral, 126 }, + { LocationNames.IndiaSouthCentral, 236 }, { LocationNames.IndonesiaCentral, 180 }, { LocationNames.IsraelCentral, 230 }, { LocationNames.IsraelNorthwest, 230 }, @@ -8749,11 +9510,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 22 }, { LocationNames.NewZealandNorth, 170 }, { LocationNames.NorthCentralUS, 22 }, + { LocationNames.NortheastUS5, 22 }, { LocationNames.NorthEurope, 110 }, { LocationNames.NorwayEast, 142 }, { LocationNames.NorwayWest, 136 }, { LocationNames.PolandCentral, 120 }, { LocationNames.QatarCentral, 230 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 274 }, { LocationNames.SouthAfricaWest, 258 }, { LocationNames.SouthCentralUS, 22 }, @@ -8834,6 +9598,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 20 }, { LocationNames.GermanyNorth, 10 }, { LocationNames.GermanyWestCentral, 8 }, + { LocationNames.IndiaSouthCentral, 126 }, { LocationNames.IndonesiaCentral, 160 }, { LocationNames.IsraelCentral, 118 }, { LocationNames.IsraelNorthwest, 118 }, @@ -8849,11 +9614,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 142 }, { LocationNames.NewZealandNorth, 252 }, { LocationNames.NorthCentralUS, 102 }, + { LocationNames.NortheastUS5, 102 }, { LocationNames.NorthEurope, 22 }, { LocationNames.NorwayEast, 22 }, { LocationNames.NorwayWest, 15 }, { LocationNames.PolandCentral, 100 }, { LocationNames.QatarCentral, 118 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 170 }, { LocationNames.SouthAfricaWest, 152 }, { LocationNames.SouthCentralUS, 112 }, @@ -8934,6 +9702,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 106 }, { LocationNames.GermanyNorth, 130 }, { LocationNames.GermanyWestCentral, 122 }, + { LocationNames.IndiaSouthCentral, 4 }, { LocationNames.IndonesiaCentral, 50 }, { LocationNames.IsraelCentral, 28 }, { LocationNames.IsraelNorthwest, 28 }, @@ -8949,11 +9718,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 210 }, { LocationNames.NewZealandNorth, 142 }, { LocationNames.NorthCentralUS, 218 }, + { LocationNames.NortheastUS5, 218 }, { LocationNames.NorthEurope, 139 }, { LocationNames.NorwayEast, 146 }, { LocationNames.NorwayWest, 140 }, { LocationNames.PolandCentral, 128 }, { LocationNames.QatarCentral, 28 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 266 }, { LocationNames.SouthAfricaWest, 270 }, { LocationNames.SouthCentralUS, 226 }, @@ -9034,6 +9806,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 148 }, { LocationNames.GermanyNorth, 153 }, { LocationNames.GermanyWestCentral, 148 }, + { LocationNames.IndiaSouthCentral, 218 }, { LocationNames.IndonesiaCentral, 168 }, { LocationNames.IsraelCentral, 244 }, { LocationNames.IsraelNorthwest, 244 }, @@ -9049,11 +9822,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 22 }, { LocationNames.NewZealandNorth, 142 }, { LocationNames.NorthCentralUS, 50 }, + { LocationNames.NortheastUS5, 50 }, { LocationNames.NorthEurope, 133 }, { LocationNames.NorwayEast, 163 }, { LocationNames.NorwayWest, 156 }, { LocationNames.PolandCentral, 144 }, { LocationNames.QatarCentral, 244 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 298 }, { LocationNames.SouthAfricaWest, 280 }, { LocationNames.SouthCentralUS, 34 }, @@ -9134,6 +9910,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 146 }, { LocationNames.GermanyNorth, 154 }, { LocationNames.GermanyWestCentral, 148 }, + { LocationNames.IndiaSouthCentral, 212 }, { LocationNames.IndonesiaCentral, 160 }, { LocationNames.IsraelCentral, 236 }, { LocationNames.IsraelNorthwest, 236 }, @@ -9149,11 +9926,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 100 }, { LocationNames.NewZealandNorth, 162 }, { LocationNames.NorthCentralUS, 44 }, + { LocationNames.NortheastUS5, 44 }, { LocationNames.NorthEurope, 136 }, { LocationNames.NorwayEast, 164 }, { LocationNames.NorwayWest, 156 }, { LocationNames.PolandCentral, 142 }, { LocationNames.QatarCentral, 236 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 296 }, { LocationNames.SouthAfricaWest, 280 }, { LocationNames.SouthCentralUS, 44 }, @@ -9234,6 +10014,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, 146 }, { LocationNames.GermanyNorth, 154 }, { LocationNames.GermanyWestCentral, 148 }, + { LocationNames.IndiaSouthCentral, 212 }, { LocationNames.IndonesiaCentral, 160 }, { LocationNames.IsraelCentral, 236 }, { LocationNames.IsraelNorthwest, 236 }, @@ -9249,11 +10030,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, 100 }, { LocationNames.NewZealandNorth, 162 }, { LocationNames.NorthCentralUS, 44 }, + { LocationNames.NortheastUS5, 44 }, { LocationNames.NorthEurope, 136 }, { LocationNames.NorwayEast, 164 }, { LocationNames.NorwayWest, 156 }, { LocationNames.PolandCentral, 142 }, { LocationNames.QatarCentral, 236 }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, 296 }, { LocationNames.SouthAfricaWest, 280 }, { LocationNames.SouthCentralUS, 44 }, @@ -9334,6 +10118,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -9349,11 +10134,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, @@ -9434,6 +10222,7 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.FranceSouth, long.MaxValue }, { LocationNames.GermanyNorth, long.MaxValue }, { LocationNames.GermanyWestCentral, long.MaxValue }, + { LocationNames.IndiaSouthCentral, long.MaxValue }, { LocationNames.IndonesiaCentral, long.MaxValue }, { LocationNames.IsraelCentral, long.MaxValue }, { LocationNames.IsraelNorthwest, long.MaxValue }, @@ -9449,11 +10238,14 @@ public static List GeneratePreferredRegionList(string sourceRegion) { LocationNames.MexicoCentral, long.MaxValue }, { LocationNames.NewZealandNorth, long.MaxValue }, { LocationNames.NorthCentralUS, long.MaxValue }, + { LocationNames.NortheastUS5, long.MaxValue }, { LocationNames.NorthEurope, long.MaxValue }, { LocationNames.NorwayEast, long.MaxValue }, { LocationNames.NorwayWest, long.MaxValue }, { LocationNames.PolandCentral, long.MaxValue }, { LocationNames.QatarCentral, long.MaxValue }, + { LocationNames.SingaporeCentral, long.MaxValue }, + { LocationNames.SingaporeNorth, long.MaxValue }, { LocationNames.SouthAfricaNorth, long.MaxValue }, { LocationNames.SouthAfricaWest, long.MaxValue }, { LocationNames.SouthCentralUS, long.MaxValue }, diff --git a/Microsoft.Azure.Cosmos/src/direct/ReplicatedResourceClient.cs b/Microsoft.Azure.Cosmos/src/direct/ReplicatedResourceClient.cs index c34ab1c06d..b6b46a361f 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ReplicatedResourceClient.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ReplicatedResourceClient.cs @@ -70,7 +70,6 @@ public ReplicatedResourceClient( bool detectClientConnectivityIssues, bool disableRetryWithRetryPolicy, bool enableReplicaValidation, - AccountConfigurationProperties accountConfigurationProperties, RetryWithConfiguration retryWithConfiguration = null, ISessionRetryOptions sessionRetryOptions = null) { @@ -102,7 +101,6 @@ public ReplicatedResourceClient( authorizationTokenProvider, useMultipleWriteLocations, enableReplicaValidation, - accountConfigurationProperties, sessionRetryOptions); this.enableReadRequestsFallback = enableReadRequestsFallback; this.useMultipleWriteLocations = useMultipleWriteLocations; @@ -380,6 +378,7 @@ internal static bool IsReadingFromMaster(ResourceType resourceType, OperationTyp resourceType == ResourceType.EncryptionScope || resourceType == ResourceType.AuthPolicyElement || resourceType == ResourceType.InteropUser || + resourceType == ResourceType.AzureRbac || #if !COSMOSCLIENT resourceType == ResourceType.Topology || operationType == OperationType.GetStorageAuthToken || @@ -434,7 +433,8 @@ internal static bool IsMasterResource(ResourceType resourceType) resourceType == ResourceType.RoleDefinition || resourceType == ResourceType.EncryptionScope || resourceType == ResourceType.Trigger || - resourceType == ResourceType.UserDefinedFunction) + resourceType == ResourceType.UserDefinedFunction || + resourceType == ResourceType.AzureRbac) { return true; } diff --git a/Microsoft.Azure.Cosmos/src/direct/RequestNameValueCollection.cs b/Microsoft.Azure.Cosmos/src/direct/RequestNameValueCollection.cs index 045279c678..5968682aa1 100644 --- a/Microsoft.Azure.Cosmos/src/direct/RequestNameValueCollection.cs +++ b/Microsoft.Azure.Cosmos/src/direct/RequestNameValueCollection.cs @@ -39,18 +39,19 @@ internal class RequestNameValueCollection : INameValueCollection public string A_IM { get; set; } public string ActivityId { get; set; } public string AddResourcePropertiesToResponse { get; set; } + public string AllowBarrierRequestWhenRWStatusRevoked { get; set; } public string AllowDocumentReadsInOfflineRegion { get; set; } + public string AllowInternalServerlessOfferRead { get; set; } public string AllowRestoreParamsUpdate { get; set; } public string AllowTentativeWrites { get; set; } -#pragma warning disable SA1025 // Code should not contain multiple whitespace in a row - public string AllowTopologyUpsertWithoutIntent { get; set; } -#pragma warning restore SA1025 // Code should not contain multiple whitespace in a row + public string AllowTopologyUpsertWithoutIntent { get; set; } public string AllowUpdatingIsPhysicalMigrationInProgress { get; set; } public string Authorization { get; set; } public string BinaryId { get; set; } public string BinaryPassthroughRequest { get; set; } public string BindReplicaDirective { get; set; } public string BuilderClientIdentifier { get; set; } + public string BypassSoftDeletionBlocking { get; set; } public string CanCharge { get; set; } public string CanOfferReplaceComplete { get; set; } public string CanThrottle { get; set; } @@ -70,7 +71,9 @@ internal class RequestNameValueCollection : INameValueCollection public string Continuation { get; set; } public string CorrelatedActivityId { get; set; } public string CosmosGatewayTransactionId { get; set; } + public string CreatePKRangesWithStatusOffline { get; set; } public string DisableRUPerMinuteUsage { get; set; } + public string DistributedTransactionId { get; set; } public string EffectivePartitionKey { get; set; } public string EmitVerboseTracesInQuery { get; set; } public string EnableConflictResolutionPolicyUpdate { get; set; } @@ -93,6 +96,7 @@ internal class RequestNameValueCollection : INameValueCollection public string GetAllPartitionKeyStatistics { get; set; } public string HighPriorityForcedBackup { get; set; } public string HttpDate { get; set; } + public string HybridLogicalClockTimestamp { get; set; } public string IfMatch { get; set; } public string IfModifiedSince { get; set; } public string IfNoneMatch { get; set; } @@ -108,6 +112,7 @@ internal class RequestNameValueCollection : INameValueCollection public string IsCassandraAlterTypeRequest { get; set; } public string IsClientEncrypted { get; set; } public string IsContinuationExpected { get; set; } + public string IsEmbeddingGeneratorRequest { get; set; } public string IsFanoutRequest { get; set; } public string IsInternalServerlessRequest { get; set; } public string IsMaterializedViewBuild { get; set; } @@ -116,9 +121,12 @@ internal class RequestNameValueCollection : INameValueCollection public string IsOfferStorageRefreshRequest { get; set; } public string IsReadOnlyScript { get; set; } public string IsRequestFromComputeNotAuthorized { get; set; } + public string IsRequestIgnoredForAutoscaleReporting { get; set; } public string IsRetriedWriteRequest { get; set; } public string IsRUPerGBEnforcementRequest { get; set; } public string IsServerlessStorageRefreshRequest { get; set; } + public string IsSoftDeletionOrRecoveryOperation { get; set; } + public string IsStrongConsistencyStoreClient { get; set; } public string IsThroughputCapRequest { get; set; } public string IsUserRequest { get; set; } public string MaxPollingIntervalMilliseconds { get; set; } @@ -130,6 +138,7 @@ internal class RequestNameValueCollection : INameValueCollection public string NoRetryOn449StatusCode { get; set; } public string OfferReplaceRURedistribution { get; set; } public string OptimisticDirectExecute { get; set; } + public string OriginalAuthTokenType { get; set; } public string PageSize { get; set; } public string ParallelizeCrossPartitionQuery { get; set; } public string PartitionCount { get; set; } @@ -139,10 +148,14 @@ internal class RequestNameValueCollection : INameValueCollection public string PopulateAnalyticalMigrationProgress { get; set; } public string PopulateBinaryEncodingMigratorProgress { get; set; } public string PopulateByokEncryptionProgress { get; set; } + public string PopulateCanFailoverManagerAccessDocumentStore { get; set; } public string PopulateCapacityType { get; set; } public string PopulateCollectionThroughputInfo { get; set; } public string PopulateCurrentPartitionThroughputInfo { get; set; } public string PopulateDocumentRecordCount { get; set; } + public string PopulateGlobalEpochRecordCount { get; set; } + public string PopulateGloballyAcceptedFailoverPolicy { get; set; } + public string PopulateGlobalStateWriteQuorumRegionsSet { get; set; } public string PopulateHighestTentativeWriteLLSN { get; set; } public string PopulateIndexMetrics { get; set; } public string PopulateIndexMetricsV2 { get; set; } @@ -172,11 +185,11 @@ internal class RequestNameValueCollection : INameValueCollection public string ProfileRequest { get; set; } public string PruneCollectionSchemas { get; set; } public string QueryVersion { get; set; } - public string ReadGlobalCommittedData { get; set; } public string RbacAction { get; set; } public string RbacResource { get; set; } public string RbacUserId { get; set; } public string ReadFeedKeyType { get; set; } + public string ReadGlobalCommittedData { get; set; } public string RemainingTimeInMsOnClientRequest { get; set; } public string RemoteStorageType { get; set; } public string RequestedCollectionType { get; set; } @@ -200,6 +213,7 @@ internal class RequestNameValueCollection : INameValueCollection public string SetMasterResourcesDeletionPending { get; set; } public string ShareThroughput { get; set; } public string ShouldBatchContinueOnError { get; set; } + public string ShouldProcessOnlyInHubRegion { get; set; } public string ShouldReturnCurrentServerDateTime { get; set; } public string SkipAdjustThroughputFractionsForOfferReplace { get; set; } public string SkipRefreshDatabaseAccountConfigs { get; set; } @@ -235,6 +249,7 @@ internal class RequestNameValueCollection : INameValueCollection public string UseSystemBudget { get; set; } public string UseUserBackgroundBudget { get; set; } public string Version { get; set; } + public string WorkloadId { get; set; } public string XDate { get; set; } public RequestNameValueCollection() @@ -479,8 +494,24 @@ public static RequestNameValueCollection BuildRequestNameValueCollectionWithKnow requestNameValueCollection.PopulateThroughputPoolInfo = nameValueCollection[HttpConstants.HttpHeaders.PopulateThroughputPoolInfo]; requestNameValueCollection.RetrieveUserStrings = nameValueCollection[WFConstants.BackendHeaders.RetrieveUserStrings]; requestNameValueCollection.PopulateVectorIndexAggregateProgress = nameValueCollection[HttpConstants.HttpHeaders.PopulateVectorIndexAggregateProgress]; - requestNameValueCollection.IfMatch = nameValueCollection[HttpConstants.HttpHeaders.IfMatch]; + requestNameValueCollection.AllowTopologyUpsertWithoutIntent = nameValueCollection[HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent]; requestNameValueCollection.ReadGlobalCommittedData = nameValueCollection[HttpConstants.HttpHeaders.ReadGlobalCommittedData]; + requestNameValueCollection.IsSoftDeletionOrRecoveryOperation = nameValueCollection[HttpConstants.HttpHeaders.IsSoftDeletionOrRecoveryOperation]; + requestNameValueCollection.WorkloadId = nameValueCollection[HttpConstants.HttpHeaders.WorkloadId]; + requestNameValueCollection.OriginalAuthTokenType = nameValueCollection[WFConstants.BackendHeaders.OriginalAuthTokenType]; + requestNameValueCollection.AllowBarrierRequestWhenRWStatusRevoked = nameValueCollection[WFConstants.BackendHeaders.AllowBarrierRequestWhenRWStatusRevoked]; + requestNameValueCollection.PopulateGlobalEpochRecordCount = nameValueCollection[WFConstants.BackendHeaders.PopulateGlobalEpochRecordCount]; + requestNameValueCollection.PopulateCanFailoverManagerAccessDocumentStore = nameValueCollection[WFConstants.BackendHeaders.PopulateCanFailoverManagerAccessDocumentStore]; + requestNameValueCollection.IsEmbeddingGeneratorRequest = nameValueCollection[HttpConstants.HttpHeaders.IsEmbeddingGeneratorRequest]; + requestNameValueCollection.PopulateGloballyAcceptedFailoverPolicy = nameValueCollection[WFConstants.BackendHeaders.PopulateGloballyAcceptedFailoverPolicy]; + requestNameValueCollection.ShouldProcessOnlyInHubRegion = nameValueCollection[HttpConstants.HttpHeaders.ShouldProcessOnlyInHubRegion]; + requestNameValueCollection.BypassSoftDeletionBlocking = nameValueCollection[HttpConstants.HttpHeaders.BypassSoftDeletionBlocking]; + requestNameValueCollection.IsStrongConsistencyStoreClient = nameValueCollection[HttpConstants.HttpHeaders.IsStrongConsistencyStoreClient]; + requestNameValueCollection.PopulateGlobalStateWriteQuorumRegionsSet = nameValueCollection[WFConstants.BackendHeaders.PopulateGlobalStateWriteQuorumRegionsSet]; + requestNameValueCollection.IsRequestIgnoredForAutoscaleReporting = nameValueCollection[HttpConstants.HttpHeaders.IsRequestIgnoredForAutoscaleReporting]; + requestNameValueCollection.AllowInternalServerlessOfferRead = nameValueCollection[HttpConstants.HttpHeaders.AllowInternalServerlessOfferRead]; + requestNameValueCollection.CreatePKRangesWithStatusOffline = nameValueCollection[HttpConstants.HttpHeaders.CreatePKRangesWithStatusOffline]; + requestNameValueCollection.IfMatch = nameValueCollection[HttpConstants.HttpHeaders.IfMatch]; requestNameValueCollection.NoRetryOn449StatusCode = nameValueCollection[HttpConstants.HttpHeaders.NoRetryOn449StatusCode]; requestNameValueCollection.SkipAdjustThroughputFractionsForOfferReplace = nameValueCollection[HttpConstants.HttpHeaders.SkipAdjustThroughputFractionsForOfferReplace]; requestNameValueCollection.SqlQueryForPartitionKeyExtraction = nameValueCollection[HttpConstants.HttpHeaders.SqlQueryForPartitionKeyExtraction]; @@ -490,7 +521,8 @@ public static RequestNameValueCollection BuildRequestNameValueCollectionWithKnow requestNameValueCollection.SupportedQueryFeatures = nameValueCollection[HttpConstants.HttpHeaders.SupportedQueryFeatures]; requestNameValueCollection.QueryVersion = nameValueCollection[HttpConstants.HttpHeaders.QueryVersion]; requestNameValueCollection.ActivityId = nameValueCollection[HttpConstants.HttpHeaders.ActivityId]; - requestNameValueCollection.AllowTopologyUpsertWithoutIntent = nameValueCollection[HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent]; + requestNameValueCollection.HybridLogicalClockTimestamp = nameValueCollection[HttpConstants.HttpHeaders.HybridLogicalClockTimestamp]; + requestNameValueCollection.DistributedTransactionId = nameValueCollection[HttpConstants.HttpHeaders.DistributedTransactionId]; } return requestNameValueCollection; @@ -530,7 +562,9 @@ public void Clear() this.A_IM = null; this.ActivityId = null; this.AddResourcePropertiesToResponse = null; + this.AllowBarrierRequestWhenRWStatusRevoked = null; this.AllowDocumentReadsInOfflineRegion = null; + this.AllowInternalServerlessOfferRead = null; this.AllowRestoreParamsUpdate = null; this.AllowTentativeWrites = null; this.AllowTopologyUpsertWithoutIntent = null; @@ -540,6 +574,7 @@ public void Clear() this.BinaryPassthroughRequest = null; this.BindReplicaDirective = null; this.BuilderClientIdentifier = null; + this.BypassSoftDeletionBlocking = null; this.CanCharge = null; this.CanOfferReplaceComplete = null; this.CanThrottle = null; @@ -559,7 +594,9 @@ public void Clear() this.Continuation = null; this.CorrelatedActivityId = null; this.CosmosGatewayTransactionId = null; + this.CreatePKRangesWithStatusOffline = null; this.DisableRUPerMinuteUsage = null; + this.DistributedTransactionId = null; this.EffectivePartitionKey = null; this.EmitVerboseTracesInQuery = null; this.EnableConflictResolutionPolicyUpdate = null; @@ -582,6 +619,7 @@ public void Clear() this.GetAllPartitionKeyStatistics = null; this.HighPriorityForcedBackup = null; this.HttpDate = null; + this.HybridLogicalClockTimestamp = null; this.IfMatch = null; this.IfModifiedSince = null; this.IfNoneMatch = null; @@ -597,6 +635,7 @@ public void Clear() this.IsCassandraAlterTypeRequest = null; this.IsClientEncrypted = null; this.IsContinuationExpected = null; + this.IsEmbeddingGeneratorRequest = null; this.IsFanoutRequest = null; this.IsInternalServerlessRequest = null; this.IsMaterializedViewBuild = null; @@ -605,9 +644,12 @@ public void Clear() this.IsOfferStorageRefreshRequest = null; this.IsReadOnlyScript = null; this.IsRequestFromComputeNotAuthorized = null; + this.IsRequestIgnoredForAutoscaleReporting = null; this.IsRetriedWriteRequest = null; this.IsRUPerGBEnforcementRequest = null; this.IsServerlessStorageRefreshRequest = null; + this.IsSoftDeletionOrRecoveryOperation = null; + this.IsStrongConsistencyStoreClient = null; this.IsThroughputCapRequest = null; this.IsUserRequest = null; this.MaxPollingIntervalMilliseconds = null; @@ -619,6 +661,7 @@ public void Clear() this.NoRetryOn449StatusCode = null; this.OfferReplaceRURedistribution = null; this.OptimisticDirectExecute = null; + this.OriginalAuthTokenType = null; this.PageSize = null; this.ParallelizeCrossPartitionQuery = null; this.PartitionCount = null; @@ -628,10 +671,14 @@ public void Clear() this.PopulateAnalyticalMigrationProgress = null; this.PopulateBinaryEncodingMigratorProgress = null; this.PopulateByokEncryptionProgress = null; + this.PopulateCanFailoverManagerAccessDocumentStore = null; this.PopulateCapacityType = null; this.PopulateCollectionThroughputInfo = null; this.PopulateCurrentPartitionThroughputInfo = null; this.PopulateDocumentRecordCount = null; + this.PopulateGlobalEpochRecordCount = null; + this.PopulateGloballyAcceptedFailoverPolicy = null; + this.PopulateGlobalStateWriteQuorumRegionsSet = null; this.PopulateHighestTentativeWriteLLSN = null; this.PopulateIndexMetrics = null; this.PopulateIndexMetricsV2 = null; @@ -661,11 +708,11 @@ public void Clear() this.ProfileRequest = null; this.PruneCollectionSchemas = null; this.QueryVersion = null; - this.ReadGlobalCommittedData = null; this.RbacAction = null; this.RbacResource = null; this.RbacUserId = null; this.ReadFeedKeyType = null; + this.ReadGlobalCommittedData = null; this.RemainingTimeInMsOnClientRequest = null; this.RemoteStorageType = null; this.RequestedCollectionType = null; @@ -689,6 +736,7 @@ public void Clear() this.SetMasterResourcesDeletionPending = null; this.ShareThroughput = null; this.ShouldBatchContinueOnError = null; + this.ShouldProcessOnlyInHubRegion = null; this.ShouldReturnCurrentServerDateTime = null; this.SkipAdjustThroughputFractionsForOfferReplace = null; this.SkipRefreshDatabaseAccountConfigs = null; @@ -724,6 +772,7 @@ public void Clear() this.UseSystemBudget = null; this.UseUserBackgroundBudget = null; this.Version = null; + this.WorkloadId = null; this.XDate = null; } @@ -735,7 +784,9 @@ public INameValueCollection Clone() A_IM = this.A_IM, ActivityId = this.ActivityId, AddResourcePropertiesToResponse = this.AddResourcePropertiesToResponse, + AllowBarrierRequestWhenRWStatusRevoked = this.AllowBarrierRequestWhenRWStatusRevoked, AllowDocumentReadsInOfflineRegion = this.AllowDocumentReadsInOfflineRegion, + AllowInternalServerlessOfferRead = this.AllowInternalServerlessOfferRead, AllowRestoreParamsUpdate = this.AllowRestoreParamsUpdate, AllowTentativeWrites = this.AllowTentativeWrites, AllowTopologyUpsertWithoutIntent = this.AllowTopologyUpsertWithoutIntent, @@ -745,6 +796,7 @@ public INameValueCollection Clone() BinaryPassthroughRequest = this.BinaryPassthroughRequest, BindReplicaDirective = this.BindReplicaDirective, BuilderClientIdentifier = this.BuilderClientIdentifier, + BypassSoftDeletionBlocking = this.BypassSoftDeletionBlocking, CanCharge = this.CanCharge, CanOfferReplaceComplete = this.CanOfferReplaceComplete, CanThrottle = this.CanThrottle, @@ -764,7 +816,9 @@ public INameValueCollection Clone() Continuation = this.Continuation, CorrelatedActivityId = this.CorrelatedActivityId, CosmosGatewayTransactionId = this.CosmosGatewayTransactionId, + CreatePKRangesWithStatusOffline = this.CreatePKRangesWithStatusOffline, DisableRUPerMinuteUsage = this.DisableRUPerMinuteUsage, + DistributedTransactionId = this.DistributedTransactionId, EffectivePartitionKey = this.EffectivePartitionKey, EmitVerboseTracesInQuery = this.EmitVerboseTracesInQuery, EnableConflictResolutionPolicyUpdate = this.EnableConflictResolutionPolicyUpdate, @@ -787,6 +841,7 @@ public INameValueCollection Clone() GetAllPartitionKeyStatistics = this.GetAllPartitionKeyStatistics, HighPriorityForcedBackup = this.HighPriorityForcedBackup, HttpDate = this.HttpDate, + HybridLogicalClockTimestamp = this.HybridLogicalClockTimestamp, IfMatch = this.IfMatch, IfModifiedSince = this.IfModifiedSince, IfNoneMatch = this.IfNoneMatch, @@ -802,6 +857,7 @@ public INameValueCollection Clone() IsCassandraAlterTypeRequest = this.IsCassandraAlterTypeRequest, IsClientEncrypted = this.IsClientEncrypted, IsContinuationExpected = this.IsContinuationExpected, + IsEmbeddingGeneratorRequest = this.IsEmbeddingGeneratorRequest, IsFanoutRequest = this.IsFanoutRequest, IsInternalServerlessRequest = this.IsInternalServerlessRequest, IsMaterializedViewBuild = this.IsMaterializedViewBuild, @@ -810,9 +866,12 @@ public INameValueCollection Clone() IsOfferStorageRefreshRequest = this.IsOfferStorageRefreshRequest, IsReadOnlyScript = this.IsReadOnlyScript, IsRequestFromComputeNotAuthorized = this.IsRequestFromComputeNotAuthorized, + IsRequestIgnoredForAutoscaleReporting = this.IsRequestIgnoredForAutoscaleReporting, IsRetriedWriteRequest = this.IsRetriedWriteRequest, IsRUPerGBEnforcementRequest = this.IsRUPerGBEnforcementRequest, IsServerlessStorageRefreshRequest = this.IsServerlessStorageRefreshRequest, + IsSoftDeletionOrRecoveryOperation = this.IsSoftDeletionOrRecoveryOperation, + IsStrongConsistencyStoreClient = this.IsStrongConsistencyStoreClient, IsThroughputCapRequest = this.IsThroughputCapRequest, IsUserRequest = this.IsUserRequest, MaxPollingIntervalMilliseconds = this.MaxPollingIntervalMilliseconds, @@ -824,6 +883,7 @@ public INameValueCollection Clone() NoRetryOn449StatusCode = this.NoRetryOn449StatusCode, OfferReplaceRURedistribution = this.OfferReplaceRURedistribution, OptimisticDirectExecute = this.OptimisticDirectExecute, + OriginalAuthTokenType = this.OriginalAuthTokenType, PageSize = this.PageSize, ParallelizeCrossPartitionQuery = this.ParallelizeCrossPartitionQuery, PartitionCount = this.PartitionCount, @@ -833,10 +893,14 @@ public INameValueCollection Clone() PopulateAnalyticalMigrationProgress = this.PopulateAnalyticalMigrationProgress, PopulateBinaryEncodingMigratorProgress = this.PopulateBinaryEncodingMigratorProgress, PopulateByokEncryptionProgress = this.PopulateByokEncryptionProgress, + PopulateCanFailoverManagerAccessDocumentStore = this.PopulateCanFailoverManagerAccessDocumentStore, PopulateCapacityType = this.PopulateCapacityType, PopulateCollectionThroughputInfo = this.PopulateCollectionThroughputInfo, PopulateCurrentPartitionThroughputInfo = this.PopulateCurrentPartitionThroughputInfo, PopulateDocumentRecordCount = this.PopulateDocumentRecordCount, + PopulateGlobalEpochRecordCount = this.PopulateGlobalEpochRecordCount, + PopulateGloballyAcceptedFailoverPolicy = this.PopulateGloballyAcceptedFailoverPolicy, + PopulateGlobalStateWriteQuorumRegionsSet = this.PopulateGlobalStateWriteQuorumRegionsSet, PopulateHighestTentativeWriteLLSN = this.PopulateHighestTentativeWriteLLSN, PopulateIndexMetrics = this.PopulateIndexMetrics, PopulateIndexMetricsV2 = this.PopulateIndexMetricsV2, @@ -866,11 +930,11 @@ public INameValueCollection Clone() ProfileRequest = this.ProfileRequest, PruneCollectionSchemas = this.PruneCollectionSchemas, QueryVersion = this.QueryVersion, - ReadGlobalCommittedData = this.ReadGlobalCommittedData, RbacAction = this.RbacAction, RbacResource = this.RbacResource, RbacUserId = this.RbacUserId, ReadFeedKeyType = this.ReadFeedKeyType, + ReadGlobalCommittedData = this.ReadGlobalCommittedData, RemainingTimeInMsOnClientRequest = this.RemainingTimeInMsOnClientRequest, RemoteStorageType = this.RemoteStorageType, RequestedCollectionType = this.RequestedCollectionType, @@ -894,6 +958,7 @@ public INameValueCollection Clone() SetMasterResourcesDeletionPending = this.SetMasterResourcesDeletionPending, ShareThroughput = this.ShareThroughput, ShouldBatchContinueOnError = this.ShouldBatchContinueOnError, + ShouldProcessOnlyInHubRegion = this.ShouldProcessOnlyInHubRegion, ShouldReturnCurrentServerDateTime = this.ShouldReturnCurrentServerDateTime, SkipAdjustThroughputFractionsForOfferReplace = this.SkipAdjustThroughputFractionsForOfferReplace, SkipRefreshDatabaseAccountConfigs = this.SkipRefreshDatabaseAccountConfigs, @@ -929,6 +994,7 @@ public INameValueCollection Clone() UseSystemBudget = this.UseSystemBudget, UseUserBackgroundBudget = this.UseUserBackgroundBudget, Version = this.Version, + WorkloadId = this.WorkloadId, XDate = this.XDate, }; @@ -1707,14 +1773,78 @@ public IEnumerable Keys() { yield return HttpConstants.HttpHeaders.PopulateVectorIndexAggregateProgress; } - if (this.IfMatch != null) + if (this.AllowTopologyUpsertWithoutIntent != null) { - yield return HttpConstants.HttpHeaders.IfMatch; + yield return HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent; } if (this.ReadGlobalCommittedData != null) { yield return HttpConstants.HttpHeaders.ReadGlobalCommittedData; } + if (this.IsSoftDeletionOrRecoveryOperation != null) + { + yield return HttpConstants.HttpHeaders.IsSoftDeletionOrRecoveryOperation; + } + if (this.WorkloadId != null) + { + yield return HttpConstants.HttpHeaders.WorkloadId; + } + if (this.OriginalAuthTokenType != null) + { + yield return WFConstants.BackendHeaders.OriginalAuthTokenType; + } + if (this.AllowBarrierRequestWhenRWStatusRevoked != null) + { + yield return WFConstants.BackendHeaders.AllowBarrierRequestWhenRWStatusRevoked; + } + if (this.PopulateGlobalEpochRecordCount != null) + { + yield return WFConstants.BackendHeaders.PopulateGlobalEpochRecordCount; + } + if (this.PopulateCanFailoverManagerAccessDocumentStore != null) + { + yield return WFConstants.BackendHeaders.PopulateCanFailoverManagerAccessDocumentStore; + } + if (this.IsEmbeddingGeneratorRequest != null) + { + yield return HttpConstants.HttpHeaders.IsEmbeddingGeneratorRequest; + } + if (this.PopulateGloballyAcceptedFailoverPolicy != null) + { + yield return WFConstants.BackendHeaders.PopulateGloballyAcceptedFailoverPolicy; + } + if (this.ShouldProcessOnlyInHubRegion != null) + { + yield return HttpConstants.HttpHeaders.ShouldProcessOnlyInHubRegion; + } + if (this.BypassSoftDeletionBlocking != null) + { + yield return HttpConstants.HttpHeaders.BypassSoftDeletionBlocking; + } + if (this.IsStrongConsistencyStoreClient != null) + { + yield return HttpConstants.HttpHeaders.IsStrongConsistencyStoreClient; + } + if (this.PopulateGlobalStateWriteQuorumRegionsSet != null) + { + yield return WFConstants.BackendHeaders.PopulateGlobalStateWriteQuorumRegionsSet; + } + if (this.IsRequestIgnoredForAutoscaleReporting != null) + { + yield return HttpConstants.HttpHeaders.IsRequestIgnoredForAutoscaleReporting; + } + if (this.AllowInternalServerlessOfferRead != null) + { + yield return HttpConstants.HttpHeaders.AllowInternalServerlessOfferRead; + } + if (this.CreatePKRangesWithStatusOffline != null) + { + yield return HttpConstants.HttpHeaders.CreatePKRangesWithStatusOffline; + } + if (this.IfMatch != null) + { + yield return HttpConstants.HttpHeaders.IfMatch; + } if (this.NoRetryOn449StatusCode != null) { yield return HttpConstants.HttpHeaders.NoRetryOn449StatusCode; @@ -1751,9 +1881,13 @@ public IEnumerable Keys() { yield return HttpConstants.HttpHeaders.ActivityId; } - if (this.AllowTopologyUpsertWithoutIntent != null) + if (this.HybridLogicalClockTimestamp != null) { - yield return HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent; + yield return HttpConstants.HttpHeaders.HybridLogicalClockTimestamp; + } + if (this.DistributedTransactionId != null) + { + yield return HttpConstants.HttpHeaders.DistributedTransactionId; } if (this.notCommonHeaders != null) @@ -2521,14 +2655,78 @@ public NameValueCollection ToNameValueCollection() { this.nameValueCollection.Add(HttpConstants.HttpHeaders.PopulateVectorIndexAggregateProgress, this.PopulateVectorIndexAggregateProgress); } - if (this.IfMatch != null) + if (this.AllowTopologyUpsertWithoutIntent != null) { - this.nameValueCollection.Add(HttpConstants.HttpHeaders.IfMatch, this.IfMatch); + this.nameValueCollection.Add(HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent, this.AllowTopologyUpsertWithoutIntent); } if (this.ReadGlobalCommittedData != null) { this.nameValueCollection.Add(HttpConstants.HttpHeaders.ReadGlobalCommittedData, this.ReadGlobalCommittedData); } + if (this.IsSoftDeletionOrRecoveryOperation != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.IsSoftDeletionOrRecoveryOperation, this.IsSoftDeletionOrRecoveryOperation); + } + if (this.WorkloadId != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.WorkloadId, this.WorkloadId); + } + if (this.OriginalAuthTokenType != null) + { + this.nameValueCollection.Add(WFConstants.BackendHeaders.OriginalAuthTokenType, this.OriginalAuthTokenType); + } + if (this.AllowBarrierRequestWhenRWStatusRevoked != null) + { + this.nameValueCollection.Add(WFConstants.BackendHeaders.AllowBarrierRequestWhenRWStatusRevoked, this.AllowBarrierRequestWhenRWStatusRevoked); + } + if (this.PopulateGlobalEpochRecordCount != null) + { + this.nameValueCollection.Add(WFConstants.BackendHeaders.PopulateGlobalEpochRecordCount, this.PopulateGlobalEpochRecordCount); + } + if (this.PopulateCanFailoverManagerAccessDocumentStore != null) + { + this.nameValueCollection.Add(WFConstants.BackendHeaders.PopulateCanFailoverManagerAccessDocumentStore, this.PopulateCanFailoverManagerAccessDocumentStore); + } + if (this.IsEmbeddingGeneratorRequest != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.IsEmbeddingGeneratorRequest, this.IsEmbeddingGeneratorRequest); + } + if (this.PopulateGloballyAcceptedFailoverPolicy != null) + { + this.nameValueCollection.Add(WFConstants.BackendHeaders.PopulateGloballyAcceptedFailoverPolicy, this.PopulateGloballyAcceptedFailoverPolicy); + } + if (this.ShouldProcessOnlyInHubRegion != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.ShouldProcessOnlyInHubRegion, this.ShouldProcessOnlyInHubRegion); + } + if (this.BypassSoftDeletionBlocking != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.BypassSoftDeletionBlocking, this.BypassSoftDeletionBlocking); + } + if (this.IsStrongConsistencyStoreClient != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.IsStrongConsistencyStoreClient, this.IsStrongConsistencyStoreClient); + } + if (this.PopulateGlobalStateWriteQuorumRegionsSet != null) + { + this.nameValueCollection.Add(WFConstants.BackendHeaders.PopulateGlobalStateWriteQuorumRegionsSet, this.PopulateGlobalStateWriteQuorumRegionsSet); + } + if (this.IsRequestIgnoredForAutoscaleReporting != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.IsRequestIgnoredForAutoscaleReporting, this.IsRequestIgnoredForAutoscaleReporting); + } + if (this.AllowInternalServerlessOfferRead != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.AllowInternalServerlessOfferRead, this.AllowInternalServerlessOfferRead); + } + if (this.CreatePKRangesWithStatusOffline != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.CreatePKRangesWithStatusOffline, this.CreatePKRangesWithStatusOffline); + } + if (this.IfMatch != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.IfMatch, this.IfMatch); + } if (this.NoRetryOn449StatusCode != null) { this.nameValueCollection.Add(HttpConstants.HttpHeaders.NoRetryOn449StatusCode, this.NoRetryOn449StatusCode); @@ -2565,9 +2763,13 @@ public NameValueCollection ToNameValueCollection() { this.nameValueCollection.Add(HttpConstants.HttpHeaders.ActivityId, this.ActivityId); } - if (this.AllowTopologyUpsertWithoutIntent != null) + if (this.HybridLogicalClockTimestamp != null) { - this.nameValueCollection.Add(HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent, this.AllowTopologyUpsertWithoutIntent); + this.nameValueCollection.Add(HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, this.HybridLogicalClockTimestamp); + } + if (this.DistributedTransactionId != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.DistributedTransactionId, this.DistributedTransactionId); } if (this.notCommonHeaders != null) { @@ -2948,11 +3150,24 @@ public string Get(string key) break; case 20: + if (object.ReferenceEquals(HttpConstants.HttpHeaders.ProfileRequest, key)) + { + return this.ProfileRequest; + } + if (object.ReferenceEquals(WFConstants.BackendHeaders.OriginalAuthTokenType, key)) + { + return this.OriginalAuthTokenType; + } if (string.Equals(HttpConstants.HttpHeaders.ProfileRequest, key, StringComparison.OrdinalIgnoreCase)) { return this.ProfileRequest; } + if (string.Equals(WFConstants.BackendHeaders.OriginalAuthTokenType, key, StringComparison.OrdinalIgnoreCase)) + { + return this.OriginalAuthTokenType; + } + break; case 21: if (object.ReferenceEquals(WFConstants.BackendHeaders.ShareThroughput, key)) @@ -3025,6 +3240,10 @@ public string Get(string key) { return this.IsReadOnlyScript; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.WorkloadId, key)) + { + return this.WorkloadId; + } if (string.Equals(HttpConstants.HttpHeaders.IndexingDirective, key, StringComparison.OrdinalIgnoreCase)) { return this.IndexingDirective; @@ -3040,6 +3259,11 @@ public string Get(string key) return this.IsReadOnlyScript; } + if (string.Equals(HttpConstants.HttpHeaders.WorkloadId, key, StringComparison.OrdinalIgnoreCase)) + { + return this.WorkloadId; + } + break; case 24: if (object.ReferenceEquals(WFConstants.BackendHeaders.CollectionServiceIndex, key)) @@ -3774,6 +3998,10 @@ public string Get(string key) { return this.ReadGlobalCommittedData; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.ShouldProcessOnlyInHubRegion, key)) + { + return this.ShouldProcessOnlyInHubRegion; + } if (string.Equals(HttpConstants.HttpHeaders.MigrateOfferToAutopilot, key, StringComparison.OrdinalIgnoreCase)) { return this.MigrateOfferToAutopilot; @@ -3799,6 +4027,11 @@ public string Get(string key) return this.ReadGlobalCommittedData; } + if (string.Equals(HttpConstants.HttpHeaders.ShouldProcessOnlyInHubRegion, key, StringComparison.OrdinalIgnoreCase)) + { + return this.ShouldProcessOnlyInHubRegion; + } + break; case 39: if (object.ReferenceEquals(HttpConstants.HttpHeaders.TruncateMergeLogRequest, key)) @@ -4061,6 +4294,10 @@ public string Get(string key) { return this.IsMigratedFixedCollection; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.IsStrongConsistencyStoreClient, key)) + { + return this.IsStrongConsistencyStoreClient; + } if (string.Equals(HttpConstants.HttpHeaders.MigrateOfferToManualThroughput, key, StringComparison.OrdinalIgnoreCase)) { return this.MigrateOfferToManualThroughput; @@ -4076,6 +4313,11 @@ public string Get(string key) return this.IsMigratedFixedCollection; } + if (string.Equals(HttpConstants.HttpHeaders.IsStrongConsistencyStoreClient, key, StringComparison.OrdinalIgnoreCase)) + { + return this.IsStrongConsistencyStoreClient; + } + break; case 47: if (object.ReferenceEquals(HttpConstants.HttpHeaders.SupportSpatialLegacyCoordinates, key)) @@ -4094,6 +4336,10 @@ public string Get(string key) { return this.IsMaterializedViewBuild; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.DistributedTransactionId, key)) + { + return this.DistributedTransactionId; + } if (string.Equals(HttpConstants.HttpHeaders.SupportSpatialLegacyCoordinates, key, StringComparison.OrdinalIgnoreCase)) { return this.SupportSpatialLegacyCoordinates; @@ -4114,6 +4360,11 @@ public string Get(string key) return this.IsMaterializedViewBuild; } + if (string.Equals(HttpConstants.HttpHeaders.DistributedTransactionId, key, StringComparison.OrdinalIgnoreCase)) + { + return this.DistributedTransactionId; + } + break; case 48: if (object.ReferenceEquals(HttpConstants.HttpHeaders.PopulateCollectionThroughputInfo, key)) @@ -4136,6 +4387,14 @@ public string Get(string key) { return this.HighPriorityForcedBackup; } + if (object.ReferenceEquals(WFConstants.BackendHeaders.PopulateGlobalEpochRecordCount, key)) + { + return this.PopulateGlobalEpochRecordCount; + } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.AllowInternalServerlessOfferRead, key)) + { + return this.AllowInternalServerlessOfferRead; + } if (string.Equals(HttpConstants.HttpHeaders.PopulateCollectionThroughputInfo, key, StringComparison.OrdinalIgnoreCase)) { return this.PopulateCollectionThroughputInfo; @@ -4161,13 +4420,36 @@ public string Get(string key) return this.HighPriorityForcedBackup; } + if (string.Equals(WFConstants.BackendHeaders.PopulateGlobalEpochRecordCount, key, StringComparison.OrdinalIgnoreCase)) + { + return this.PopulateGlobalEpochRecordCount; + } + + if (string.Equals(HttpConstants.HttpHeaders.AllowInternalServerlessOfferRead, key, StringComparison.OrdinalIgnoreCase)) + { + return this.AllowInternalServerlessOfferRead; + } + break; case 49: + if (object.ReferenceEquals(HttpConstants.HttpHeaders.UsePolygonsSmallerThanAHemisphere, key)) + { + return this.UsePolygonsSmallerThanAHemisphere; + } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.BypassSoftDeletionBlocking, key)) + { + return this.BypassSoftDeletionBlocking; + } if (string.Equals(HttpConstants.HttpHeaders.UsePolygonsSmallerThanAHemisphere, key, StringComparison.OrdinalIgnoreCase)) { return this.UsePolygonsSmallerThanAHemisphere; } + if (string.Equals(HttpConstants.HttpHeaders.BypassSoftDeletionBlocking, key, StringComparison.OrdinalIgnoreCase)) + { + return this.BypassSoftDeletionBlocking; + } + break; case 50: if (object.ReferenceEquals(HttpConstants.HttpHeaders.ResponseContinuationTokenLimitInKB, key)) @@ -4211,6 +4493,18 @@ public string Get(string key) { return this.PopulateDocumentRecordCount; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.IsEmbeddingGeneratorRequest, key)) + { + return this.IsEmbeddingGeneratorRequest; + } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.CreatePKRangesWithStatusOffline, key)) + { + return this.CreatePKRangesWithStatusOffline; + } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, key)) + { + return this.HybridLogicalClockTimestamp; + } if (string.Equals(HttpConstants.HttpHeaders.EnableLowPrecisionOrderBy, key, StringComparison.OrdinalIgnoreCase)) { return this.EnableLowPrecisionOrderBy; @@ -4226,6 +4520,21 @@ public string Get(string key) return this.PopulateDocumentRecordCount; } + if (string.Equals(HttpConstants.HttpHeaders.IsEmbeddingGeneratorRequest, key, StringComparison.OrdinalIgnoreCase)) + { + return this.IsEmbeddingGeneratorRequest; + } + + if (string.Equals(HttpConstants.HttpHeaders.CreatePKRangesWithStatusOffline, key, StringComparison.OrdinalIgnoreCase)) + { + return this.CreatePKRangesWithStatusOffline; + } + + if (string.Equals(HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, key, StringComparison.OrdinalIgnoreCase)) + { + return this.HybridLogicalClockTimestamp; + } + break; case 52: if (object.ReferenceEquals(HttpConstants.HttpHeaders.OfferReplaceRURedistribution, key)) @@ -4297,6 +4606,10 @@ public string Get(string key) { return this.PopulateVectorIndexAggregateProgress; } + if (object.ReferenceEquals(WFConstants.BackendHeaders.PopulateGloballyAcceptedFailoverPolicy, key)) + { + return this.PopulateGloballyAcceptedFailoverPolicy; + } if (string.Equals(HttpConstants.HttpHeaders.IncludePhysicalPartitionThroughputInfo, key, StringComparison.OrdinalIgnoreCase)) { return this.IncludePhysicalPartitionThroughputInfo; @@ -4322,6 +4635,11 @@ public string Get(string key) return this.PopulateVectorIndexAggregateProgress; } + if (string.Equals(WFConstants.BackendHeaders.PopulateGloballyAcceptedFailoverPolicy, key, StringComparison.OrdinalIgnoreCase)) + { + return this.PopulateGloballyAcceptedFailoverPolicy; + } + break; case 55: if (object.ReferenceEquals(HttpConstants.HttpHeaders.UpdateOfferStateToRestorePending, key)) @@ -4351,11 +4669,20 @@ public string Get(string key) break; case 57: + if (object.ReferenceEquals(WFConstants.BackendHeaders.PopulateUnflushedMergeEntryCount, key)) + { + return this.PopulateUnflushedMergeEntryCount; + } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent, key)) + { + return this.AllowTopologyUpsertWithoutIntent; + } if (string.Equals(WFConstants.BackendHeaders.PopulateUnflushedMergeEntryCount, key, StringComparison.OrdinalIgnoreCase)) { return this.PopulateUnflushedMergeEntryCount; } - if (string.Equals(WFConstants.BackendHeaders.AllowTopologyUpsertWithoutIntent, key, StringComparison.OrdinalIgnoreCase)) + + if (string.Equals(HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent, key, StringComparison.OrdinalIgnoreCase)) { return this.AllowTopologyUpsertWithoutIntent; } @@ -4374,6 +4701,10 @@ public string Get(string key) { return this.PopulateHighestTentativeWriteLLSN; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.IsSoftDeletionOrRecoveryOperation, key)) + { + return this.IsSoftDeletionOrRecoveryOperation; + } if (string.Equals(HttpConstants.HttpHeaders.IgnoreSystemLoweringMaxThroughput, key, StringComparison.OrdinalIgnoreCase)) { return this.IgnoreSystemLoweringMaxThroughput; @@ -4389,6 +4720,11 @@ public string Get(string key) return this.PopulateHighestTentativeWriteLLSN; } + if (string.Equals(HttpConstants.HttpHeaders.IsSoftDeletionOrRecoveryOperation, key, StringComparison.OrdinalIgnoreCase)) + { + return this.IsSoftDeletionOrRecoveryOperation; + } + break; case 59: if (object.ReferenceEquals(HttpConstants.HttpHeaders.UpdateMaxThroughputEverProvisioned, key)) @@ -4418,6 +4754,13 @@ public string Get(string key) return this.AllowDocumentReadsInOfflineRegion; } + break; + case 60: + if (string.Equals(WFConstants.BackendHeaders.PopulateGlobalStateWriteQuorumRegionsSet, key, StringComparison.OrdinalIgnoreCase)) + { + return this.PopulateGlobalStateWriteQuorumRegionsSet; + } + break; case 61: if (string.Equals(HttpConstants.HttpHeaders.IsServerlessStorageRefreshRequest, key, StringComparison.OrdinalIgnoreCase)) @@ -4432,13 +4775,40 @@ public string Get(string key) return this.SkipAdjustThroughputFractionsForOfferReplace; } + break; + case 63: + if (string.Equals(HttpConstants.HttpHeaders.IsRequestIgnoredForAutoscaleReporting, key, StringComparison.OrdinalIgnoreCase)) + { + return this.IsRequestIgnoredForAutoscaleReporting; + } + + break; + case 64: + if (string.Equals(WFConstants.BackendHeaders.AllowBarrierRequestWhenRWStatusRevoked, key, StringComparison.OrdinalIgnoreCase)) + { + return this.AllowBarrierRequestWhenRWStatusRevoked; + } + break; case 65: + if (object.ReferenceEquals(HttpConstants.HttpHeaders.AllowUpdatingIsPhysicalMigrationInProgress, key)) + { + return this.AllowUpdatingIsPhysicalMigrationInProgress; + } + if (object.ReferenceEquals(WFConstants.BackendHeaders.PopulateCanFailoverManagerAccessDocumentStore, key)) + { + return this.PopulateCanFailoverManagerAccessDocumentStore; + } if (string.Equals(HttpConstants.HttpHeaders.AllowUpdatingIsPhysicalMigrationInProgress, key, StringComparison.OrdinalIgnoreCase)) { return this.AllowUpdatingIsPhysicalMigrationInProgress; } + if (string.Equals(WFConstants.BackendHeaders.PopulateCanFailoverManagerAccessDocumentStore, key, StringComparison.OrdinalIgnoreCase)) + { + return this.PopulateCanFailoverManagerAccessDocumentStore; + } + break; case 71: if (string.Equals(HttpConstants.HttpHeaders.UpdateOfferStateToPendingForThroughputSplit, key, StringComparison.OrdinalIgnoreCase)) @@ -5225,6 +5595,26 @@ public void UpdateHelper( } break; case 20: + if (object.ReferenceEquals(HttpConstants.HttpHeaders.ProfileRequest, key)) + { + if (throwIfAlreadyExists && this.ProfileRequest != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.ProfileRequest = value; + return; + } + if (object.ReferenceEquals(WFConstants.BackendHeaders.OriginalAuthTokenType, key)) + { + if (throwIfAlreadyExists && this.OriginalAuthTokenType != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.OriginalAuthTokenType = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.ProfileRequest, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.ProfileRequest != null) @@ -5235,6 +5625,16 @@ public void UpdateHelper( this.ProfileRequest = value; return; } + if (string.Equals(WFConstants.BackendHeaders.OriginalAuthTokenType, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.OriginalAuthTokenType != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.OriginalAuthTokenType = value; + return; + } break; case 21: if (object.ReferenceEquals(WFConstants.BackendHeaders.ShareThroughput, key)) @@ -5391,6 +5791,16 @@ public void UpdateHelper( this.IsReadOnlyScript = value; return; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.WorkloadId, key)) + { + if (throwIfAlreadyExists && this.WorkloadId != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.WorkloadId = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.IndexingDirective, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.IndexingDirective != null) @@ -5421,6 +5831,16 @@ public void UpdateHelper( this.IsReadOnlyScript = value; return; } + if (string.Equals(HttpConstants.HttpHeaders.WorkloadId, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.WorkloadId != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.WorkloadId = value; + return; + } break; case 24: if (object.ReferenceEquals(WFConstants.BackendHeaders.CollectionServiceIndex, key)) @@ -7021,6 +7441,16 @@ public void UpdateHelper( this.ReadGlobalCommittedData = value; return; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.ShouldProcessOnlyInHubRegion, key)) + { + if (throwIfAlreadyExists && this.ShouldProcessOnlyInHubRegion != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.ShouldProcessOnlyInHubRegion = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.MigrateOfferToAutopilot, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.MigrateOfferToAutopilot != null) @@ -7071,6 +7501,16 @@ public void UpdateHelper( this.ReadGlobalCommittedData = value; return; } + if (string.Equals(HttpConstants.HttpHeaders.ShouldProcessOnlyInHubRegion, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.ShouldProcessOnlyInHubRegion != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.ShouldProcessOnlyInHubRegion = value; + return; + } break; case 39: if (object.ReferenceEquals(HttpConstants.HttpHeaders.TruncateMergeLogRequest, key)) @@ -7637,6 +8077,16 @@ public void UpdateHelper( this.IsMigratedFixedCollection = value; return; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.IsStrongConsistencyStoreClient, key)) + { + if (throwIfAlreadyExists && this.IsStrongConsistencyStoreClient != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.IsStrongConsistencyStoreClient = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.MigrateOfferToManualThroughput, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.MigrateOfferToManualThroughput != null) @@ -7667,6 +8117,16 @@ public void UpdateHelper( this.IsMigratedFixedCollection = value; return; } + if (string.Equals(HttpConstants.HttpHeaders.IsStrongConsistencyStoreClient, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.IsStrongConsistencyStoreClient != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.IsStrongConsistencyStoreClient = value; + return; + } break; case 47: if (object.ReferenceEquals(HttpConstants.HttpHeaders.SupportSpatialLegacyCoordinates, key)) @@ -7709,6 +8169,16 @@ public void UpdateHelper( this.IsMaterializedViewBuild = value; return; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.DistributedTransactionId, key)) + { + if (throwIfAlreadyExists && this.DistributedTransactionId != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.DistributedTransactionId = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.SupportSpatialLegacyCoordinates, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.SupportSpatialLegacyCoordinates != null) @@ -7749,6 +8219,16 @@ public void UpdateHelper( this.IsMaterializedViewBuild = value; return; } + if (string.Equals(HttpConstants.HttpHeaders.DistributedTransactionId, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.DistributedTransactionId != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.DistributedTransactionId = value; + return; + } break; case 48: if (object.ReferenceEquals(HttpConstants.HttpHeaders.PopulateCollectionThroughputInfo, key)) @@ -7801,6 +8281,26 @@ public void UpdateHelper( this.HighPriorityForcedBackup = value; return; } + if (object.ReferenceEquals(WFConstants.BackendHeaders.PopulateGlobalEpochRecordCount, key)) + { + if (throwIfAlreadyExists && this.PopulateGlobalEpochRecordCount != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.PopulateGlobalEpochRecordCount = value; + return; + } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.AllowInternalServerlessOfferRead, key)) + { + if (throwIfAlreadyExists && this.AllowInternalServerlessOfferRead != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.AllowInternalServerlessOfferRead = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.PopulateCollectionThroughputInfo, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.PopulateCollectionThroughputInfo != null) @@ -7851,8 +8351,48 @@ public void UpdateHelper( this.HighPriorityForcedBackup = value; return; } + if (string.Equals(WFConstants.BackendHeaders.PopulateGlobalEpochRecordCount, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.PopulateGlobalEpochRecordCount != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.PopulateGlobalEpochRecordCount = value; + return; + } + if (string.Equals(HttpConstants.HttpHeaders.AllowInternalServerlessOfferRead, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.AllowInternalServerlessOfferRead != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.AllowInternalServerlessOfferRead = value; + return; + } break; case 49: + if (object.ReferenceEquals(HttpConstants.HttpHeaders.UsePolygonsSmallerThanAHemisphere, key)) + { + if (throwIfAlreadyExists && this.UsePolygonsSmallerThanAHemisphere != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.UsePolygonsSmallerThanAHemisphere = value; + return; + } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.BypassSoftDeletionBlocking, key)) + { + if (throwIfAlreadyExists && this.BypassSoftDeletionBlocking != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.BypassSoftDeletionBlocking = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.UsePolygonsSmallerThanAHemisphere, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.UsePolygonsSmallerThanAHemisphere != null) @@ -7863,6 +8403,16 @@ public void UpdateHelper( this.UsePolygonsSmallerThanAHemisphere = value; return; } + if (string.Equals(HttpConstants.HttpHeaders.BypassSoftDeletionBlocking, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.BypassSoftDeletionBlocking != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.BypassSoftDeletionBlocking = value; + return; + } break; case 50: if (object.ReferenceEquals(HttpConstants.HttpHeaders.ResponseContinuationTokenLimitInKB, key)) @@ -7957,6 +8507,36 @@ public void UpdateHelper( this.PopulateDocumentRecordCount = value; return; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.IsEmbeddingGeneratorRequest, key)) + { + if (throwIfAlreadyExists && this.IsEmbeddingGeneratorRequest != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.IsEmbeddingGeneratorRequest = value; + return; + } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.CreatePKRangesWithStatusOffline, key)) + { + if (throwIfAlreadyExists && this.CreatePKRangesWithStatusOffline != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.CreatePKRangesWithStatusOffline = value; + return; + } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, key)) + { + if (throwIfAlreadyExists && this.HybridLogicalClockTimestamp != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.HybridLogicalClockTimestamp = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.EnableLowPrecisionOrderBy, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.EnableLowPrecisionOrderBy != null) @@ -7987,6 +8567,36 @@ public void UpdateHelper( this.PopulateDocumentRecordCount = value; return; } + if (string.Equals(HttpConstants.HttpHeaders.IsEmbeddingGeneratorRequest, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.IsEmbeddingGeneratorRequest != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.IsEmbeddingGeneratorRequest = value; + return; + } + if (string.Equals(HttpConstants.HttpHeaders.CreatePKRangesWithStatusOffline, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.CreatePKRangesWithStatusOffline != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.CreatePKRangesWithStatusOffline = value; + return; + } + if (string.Equals(HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.HybridLogicalClockTimestamp != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.HybridLogicalClockTimestamp = value; + return; + } break; case 52: if (object.ReferenceEquals(HttpConstants.HttpHeaders.OfferReplaceRURedistribution, key)) @@ -8143,6 +8753,16 @@ public void UpdateHelper( this.PopulateVectorIndexAggregateProgress = value; return; } + if (object.ReferenceEquals(WFConstants.BackendHeaders.PopulateGloballyAcceptedFailoverPolicy, key)) + { + if (throwIfAlreadyExists && this.PopulateGloballyAcceptedFailoverPolicy != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.PopulateGloballyAcceptedFailoverPolicy = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.IncludePhysicalPartitionThroughputInfo, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.IncludePhysicalPartitionThroughputInfo != null) @@ -8193,6 +8813,16 @@ public void UpdateHelper( this.PopulateVectorIndexAggregateProgress = value; return; } + if (string.Equals(WFConstants.BackendHeaders.PopulateGloballyAcceptedFailoverPolicy, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.PopulateGloballyAcceptedFailoverPolicy != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.PopulateGloballyAcceptedFailoverPolicy = value; + return; + } break; case 55: if (object.ReferenceEquals(HttpConstants.HttpHeaders.UpdateOfferStateToRestorePending, key)) @@ -8249,6 +8879,26 @@ public void UpdateHelper( } break; case 57: + if (object.ReferenceEquals(WFConstants.BackendHeaders.PopulateUnflushedMergeEntryCount, key)) + { + if (throwIfAlreadyExists && this.PopulateUnflushedMergeEntryCount != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.PopulateUnflushedMergeEntryCount = value; + return; + } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent, key)) + { + if (throwIfAlreadyExists && this.AllowTopologyUpsertWithoutIntent != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.AllowTopologyUpsertWithoutIntent = value; + return; + } if (string.Equals(WFConstants.BackendHeaders.PopulateUnflushedMergeEntryCount, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.PopulateUnflushedMergeEntryCount != null) @@ -8259,7 +8909,7 @@ public void UpdateHelper( this.PopulateUnflushedMergeEntryCount = value; return; } - if (string.Equals(WFConstants.BackendHeaders.AllowTopologyUpsertWithoutIntent, key, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.AllowTopologyUpsertWithoutIntent != null) { @@ -8301,6 +8951,16 @@ public void UpdateHelper( this.PopulateHighestTentativeWriteLLSN = value; return; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.IsSoftDeletionOrRecoveryOperation, key)) + { + if (throwIfAlreadyExists && this.IsSoftDeletionOrRecoveryOperation != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.IsSoftDeletionOrRecoveryOperation = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.IgnoreSystemLoweringMaxThroughput, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.IgnoreSystemLoweringMaxThroughput != null) @@ -8331,6 +8991,16 @@ public void UpdateHelper( this.PopulateHighestTentativeWriteLLSN = value; return; } + if (string.Equals(HttpConstants.HttpHeaders.IsSoftDeletionOrRecoveryOperation, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.IsSoftDeletionOrRecoveryOperation != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.IsSoftDeletionOrRecoveryOperation = value; + return; + } break; case 59: if (object.ReferenceEquals(HttpConstants.HttpHeaders.UpdateMaxThroughputEverProvisioned, key)) @@ -8394,6 +9064,18 @@ public void UpdateHelper( return; } break; + case 60: + if (string.Equals(WFConstants.BackendHeaders.PopulateGlobalStateWriteQuorumRegionsSet, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.PopulateGlobalStateWriteQuorumRegionsSet != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.PopulateGlobalStateWriteQuorumRegionsSet = value; + return; + } + break; case 61: if (string.Equals(HttpConstants.HttpHeaders.IsServerlessStorageRefreshRequest, key, StringComparison.OrdinalIgnoreCase)) { @@ -8418,7 +9100,51 @@ public void UpdateHelper( return; } break; + case 63: + if (string.Equals(HttpConstants.HttpHeaders.IsRequestIgnoredForAutoscaleReporting, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.IsRequestIgnoredForAutoscaleReporting != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.IsRequestIgnoredForAutoscaleReporting = value; + return; + } + break; + case 64: + if (string.Equals(WFConstants.BackendHeaders.AllowBarrierRequestWhenRWStatusRevoked, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.AllowBarrierRequestWhenRWStatusRevoked != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.AllowBarrierRequestWhenRWStatusRevoked = value; + return; + } + break; case 65: + if (object.ReferenceEquals(HttpConstants.HttpHeaders.AllowUpdatingIsPhysicalMigrationInProgress, key)) + { + if (throwIfAlreadyExists && this.AllowUpdatingIsPhysicalMigrationInProgress != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.AllowUpdatingIsPhysicalMigrationInProgress = value; + return; + } + if (object.ReferenceEquals(WFConstants.BackendHeaders.PopulateCanFailoverManagerAccessDocumentStore, key)) + { + if (throwIfAlreadyExists && this.PopulateCanFailoverManagerAccessDocumentStore != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.PopulateCanFailoverManagerAccessDocumentStore = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.AllowUpdatingIsPhysicalMigrationInProgress, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.AllowUpdatingIsPhysicalMigrationInProgress != null) @@ -8429,6 +9155,16 @@ public void UpdateHelper( this.AllowUpdatingIsPhysicalMigrationInProgress = value; return; } + if (string.Equals(WFConstants.BackendHeaders.PopulateCanFailoverManagerAccessDocumentStore, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.PopulateCanFailoverManagerAccessDocumentStore != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.PopulateCanFailoverManagerAccessDocumentStore = value; + return; + } break; case 71: if (string.Equals(HttpConstants.HttpHeaders.UpdateOfferStateToPendingForThroughputSplit, key, StringComparison.OrdinalIgnoreCase)) diff --git a/Microsoft.Azure.Cosmos/src/direct/RequestOptions.cs b/Microsoft.Azure.Cosmos/src/direct/RequestOptions.cs index a06cba420d..e27ee00cf3 100644 --- a/Microsoft.Azure.Cosmos/src/direct/RequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/direct/RequestOptions.cs @@ -72,7 +72,6 @@ sealed class RequestOptions /// public IList PostTriggerInclude { get; set; } -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved /// /// Gets or sets the condition (ETag) associated with the request in the Azure Cosmos DB service. /// @@ -151,9 +150,7 @@ sealed class RequestOptions /// /// public ConsistencyLevel? ConsistencyLevel { get; set; } -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved /// /// Gets or sets the token for use with session consistency in the Azure Cosmos DB service. /// @@ -210,9 +207,7 @@ sealed class RequestOptions /// /// public string SessionToken { get; set; } -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved /// /// Gets or sets the expiry time for resource token. Used when creating/updating/reading permissions in the Azure Cosmos DB service. /// @@ -233,7 +228,6 @@ sealed class RequestOptions /// /// public int? ResourceTokenExpirySeconds { get; set; } -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved /// /// Gets or sets the offer type for the resource in the Azure Cosmos DB service. @@ -372,9 +366,7 @@ sealed class RequestOptions /// public PartitionKey PartitionKey { get; set; } - -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -/// + /// /// Gets or sets the for the current request in the Azure Cosmos DB service. /// /// @@ -403,7 +395,6 @@ sealed class RequestOptions /// /// public bool EnableScriptLogging { get; set; } -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved /// /// Gets or sets for stored procedure execution requests in Azure Cosmos DB. @@ -476,9 +467,7 @@ sealed class RequestOptions /// public bool PopulatePartitionKeyRangeStatistics { get; set; } - -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -/// + /// /// Gets or sets the for document collection read unique index reindex progress. /// /// @@ -490,11 +479,8 @@ sealed class RequestOptions /// For usage, please refer to the example in . /// internal bool PopulateUniqueIndexReIndexProgress { get; set; } -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved - -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -/// + /// /// Gets or sets the for document collection read requests. /// /// @@ -506,7 +492,6 @@ sealed class RequestOptions /// For usage, please refer to the example in . /// internal bool PopulateAnalyticalMigrationProgress { get; set; } -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved /// /// Gets or sets the for document collection read requests. @@ -524,14 +509,31 @@ sealed class RequestOptions internal bool AllowUpdatingIsPhysicalMigrationInProgress { get; set; } /// - /// Gets or sets the flag for a topology resource upsert to bypass the use + /// Gets or sets the flag for a topology resource upsert to bypass the use /// of intent on an account with per-partition automatic failover (PPAF) enabled. /// internal bool AllowTopologyUpsertWithoutIntent { get; set; } -#pragma warning disable CS1570 -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -#pragma warning disable SA1004 // Documentation lines should begin with single space + /// + /// Gets or sets the , Inidicates whether the operation is a soft deletion operation. + /// + internal bool IsSoftDeletionOrRecoveryOperation { get; set; } + + /// + /// Gets or sets the , Inidicates whether softdeletion blocking should be bypassed. + /// + internal bool BypassSoftDeletionBlocking { get; set; } + + /// + /// Gets or sets the , Inidicates whether the request is coming from strong consistency store client. + /// + internal bool IsStrongConsistencyStoreClient { get; set; } + + /// + /// Gets or sets the , Indicates whether the request should retry in the current hub region. + /// + internal bool ShouldProcessOnlyInHubRegion { get; set; } + /// /// Gets or sets the for document collection read requests. /// @@ -543,12 +545,12 @@ sealed class RequestOptions /// /// For usage, please refer to the example in . /// -#pragma warning disable SA1606 // Element documentation should have summary text internal bool PopulateBYOKEncryptionProgress { get; set; } -#pragma warning restore SA1004 // Documentation lines should begin with single space -#pragma warning restore SA1606 // Element documentation should have summary text -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved -#pragma warning restore CS1570 + + /// + /// Gets or sets the for document requests. + /// + internal bool IsEmbeddingGeneratorRequest { get; set; } /// /// Gets or sets the Remote storage enablement @@ -802,6 +804,17 @@ sealed class RequestOptions /// /// internal Collection ThroughputBucketLimits { get; set; } + + + /// + /// Gets or sets the auth token override to use for this request. + /// + /// + /// + /// is used to explicitly set the auth header to use for this request. + /// + /// + internal string AuthTokenOverride { get; set; } #endif } diff --git a/Microsoft.Azure.Cosmos/src/direct/RequestTimeoutException.cs b/Microsoft.Azure.Cosmos/src/direct/RequestTimeoutException.cs index ea28640fd7..d7b6c05237 100644 --- a/Microsoft.Azure.Cosmos/src/direct/RequestTimeoutException.cs +++ b/Microsoft.Azure.Cosmos/src/direct/RequestTimeoutException.cs @@ -14,25 +14,35 @@ namespace Microsoft.Azure.Documents internal sealed class RequestTimeoutException : DocumentClientException { public RequestTimeoutException() - : this(RMResources.RequestTimeout) + : this(RMResources.RequestTimeout, SubStatusCodes.Unknown) { } public RequestTimeoutException(string message, Uri requestUri = null) - : this(message, null, null, requestUri) + : this(message, null, null, SubStatusCodes.Unknown, requestUri) { } + public RequestTimeoutException(string message, SubStatusCodes subStatusCode) + : this(message, null, null, subStatusCode, null) + { + } + + public RequestTimeoutException(string message, SubStatusCodes subStatusCode, Uri requestUri = null) + : this(message, null, null, subStatusCode, requestUri) + { + } + public RequestTimeoutException(string message, Exception innerException, Uri requestUri = null) - : this(message, innerException, null, requestUri) + : this(message, innerException, null, SubStatusCodes.Unknown, requestUri) { } public RequestTimeoutException(string message, HttpResponseHeaders headers, Uri requestUri = null) - : this(message, null, headers, requestUri) + : this(message, null, headers, SubStatusCodes.Unknown, requestUri) { } @@ -44,23 +54,23 @@ public RequestTimeoutException(Exception innerException, Uri requestUri = null) } public RequestTimeoutException(string message, INameValueCollection headers, Uri requestUri = null) - : base(message, null, headers, HttpStatusCode.RequestTimeout, requestUri) + : base(message, null, headers, HttpStatusCode.RequestTimeout, SubStatusCodes.Unknown, requestUri) { SetDescription(); } public RequestTimeoutException(string message, Exception innerException, Uri requestUri = null, string localIpAddress = null) - : this(message, innerException, (HttpResponseHeaders)null, requestUri) + : this(message, innerException, (HttpResponseHeaders)null, SubStatusCodes.Unknown, requestUri) { this.LocalIp = localIpAddress; } - public RequestTimeoutException(string message, Exception innerException, HttpResponseHeaders headers, + SubStatusCodes? subStatusCode, Uri requestUri = null) - : base(message, innerException, headers, HttpStatusCode.RequestTimeout, requestUri) + : base(message, innerException, headers, HttpStatusCode.RequestTimeout, requestUri, subStatusCode) { SetDescription(); } @@ -101,4 +111,4 @@ private void SetDescription() this.StatusDescription = HttpConstants.HttpStatusDescriptions.RequestTimeout; } } -} +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/direct/Resource.cs b/Microsoft.Azure.Cosmos/src/direct/Resource.cs index 38fbd51e21..f04a71c0ee 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Resource.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Resource.cs @@ -43,9 +43,7 @@ protected Resource(Resource resource) } - -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -/// + /// /// Gets or sets the Id of the resource in the Azure Cosmos DB service. /// /// The Id associated with the resource. @@ -67,7 +65,6 @@ protected Resource(Resource resource) /// /// [JsonProperty(PropertyName = Constants.Properties.Id)] -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved public virtual string Id { get diff --git a/Microsoft.Azure.Cosmos/src/direct/ResourceId.cs b/Microsoft.Azure.Cosmos/src/direct/ResourceId.cs index 94f68d5f45..126cfeb223 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ResourceId.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ResourceId.cs @@ -550,6 +550,30 @@ public bool IsInteropUserId } } + public ulong AzureRbac + { + get; + private set; + } + + public ResourceId AzureRbacId + { + get + { + ResourceId rid = new ResourceId(); + rid.AzureRbac = this.AzureRbac; + return rid; + } + } + + public bool IsAzureRbacId + { + get + { + return this.AzureRbac != 0; + } + } + public byte[] Value { get @@ -569,6 +593,8 @@ public byte[] Value len += ResourceId.RbacResourceIdLength; else if (this.InteropUser > 0) len += ResourceId.RbacResourceIdLength; + else if (this.AzureRbac > 0) + len += ResourceId.RbacResourceIdLength; else if (this.Database > 0) len += 4; if (this.DocumentCollection > 0 || this.User > 0 || this.UserDefinedType > 0 || this.ClientEncryptionKey > 0) @@ -610,6 +636,11 @@ public byte[] Value ResourceId.BlockCopy(BitConverter.GetBytes(this.InteropUser), 0, val, 0, ResourceId.RbacResourceIdLength); ResourceId.BlockCopy(BitConverter.GetBytes(0x2000), 0, val, 4, 2); } + else if (this.AzureRbac > 0) + { + ResourceId.BlockCopy(BitConverter.GetBytes(this.AzureRbac), 0, val, 0, ResourceId.RbacResourceIdLength); + ResourceId.BlockCopy(BitConverter.GetBytes(0x4000), 0, val, 4, 2); + } if (this.DocumentCollection > 0) ResourceId.BlockCopy(BitConverter.GetBytes(this.DocumentCollection), 0, val, 4, 4); @@ -732,6 +763,14 @@ public static ResourceId NewInteropUserId(ulong interopUserId) }; } + public static ResourceId NewAzureRbacId(ulong azureRbacId) + { + return new ResourceId() + { + AzureRbac = azureRbacId + }; + } + public static ResourceId NewDocumentCollectionId(string databaseId, uint collectionId) { ResourceId dbId = ResourceId.Parse(databaseId); @@ -976,6 +1015,10 @@ public static bool TryParse(string id, out ResourceId rid) rid.InteropUser = rbacResourceId; break; + case RbacResourceType.RbacResourceType_AzureRbac: + rid.AzureRbac = rbacResourceId; + break; + default: return false; } @@ -1287,6 +1330,7 @@ internal enum RbacResourceType : byte RbacResourceType_RoleAssignment = 0x10, RbacResourceType_InteropUser = 0x20, RbacResourceType_AuthPolicyElement = 0x30, + RbacResourceType_AzureRbac = 0x40, } } } diff --git a/Microsoft.Azure.Cosmos/src/direct/ResourceOperation.cs b/Microsoft.Azure.Cosmos/src/direct/ResourceOperation.cs index 7a745df7b5..2a2dacb8be 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ResourceOperation.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ResourceOperation.cs @@ -125,8 +125,8 @@ public ResourceOperation( public static readonly ResourceOperation Resume = new ResourceOperation(OperationType.Resume, ResourceType.Replica); public static readonly ResourceOperation Stop = new ResourceOperation(OperationType.Stop, ResourceType.Replica); public static readonly ResourceOperation Recycle = new ResourceOperation(OperationType.Recycle, ResourceType.Replica); - - // TODO: Add following for master partition once we support master partition migration. + + public static readonly ResourceOperation AbortMasterPartitionMigration = new ResourceOperation(OperationType.AbortPartitionMigration, ResourceType.MasterPartition); public static readonly ResourceOperation AbortServerPartitionMigration = new ResourceOperation(OperationType.AbortPartitionMigration, ResourceType.ServerPartition); public static readonly ResourceOperation CompleteServerPartitionMigration = new ResourceOperation(OperationType.CompleteSplit, ResourceType.ServerPartition); diff --git a/Microsoft.Azure.Cosmos/src/direct/ResourceResponse.cs b/Microsoft.Azure.Cosmos/src/direct/ResourceResponse.cs index 5d7b4206ab..33aebe31bc 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ResourceResponse.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ResourceResponse.cs @@ -3,9 +3,7 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Documents.Client { - -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -/// + /// /// Represents the template class used by methods returning single objects in the Azure Cosmos DB service. /// /// the resource type. @@ -29,16 +27,13 @@ namespace Microsoft.Azure.Documents.Client /// #if COSMOSCLIENT internal -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved #else public #endif class ResourceResponse : ResourceResponseBase, IResourceResponse where TResource : Resource, new() { private TResource resource; -#pragma warning disable IDE0044 // Add readonly modifier private ITypeResolver typeResolver; -#pragma warning restore IDE0044 // Add readonly modifier /// /// Constructor exposed for mocking purposes for the Azure Cosmos DB service. diff --git a/Microsoft.Azure.Cosmos/src/direct/ResourceResponseBase.cs b/Microsoft.Azure.Cosmos/src/direct/ResourceResponseBase.cs index 21dbe28b04..f149890bef 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ResourceResponseBase.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ResourceResponseBase.cs @@ -25,12 +25,8 @@ abstract class ResourceResponseBase : IResourceResponseBase // Ideally response was intended to be marked as protected(to be accessed by sub-classes) but since DocumentServiceResponse class is marked internal, // it gives Inconsistent accessibility error saying DocumentServiceResponse is less accessible than field ServiceResponse.response if I mark it as protected. internal DocumentServiceResponse response; -#pragma warning disable IDE0044 // Add readonly modifier private Dictionary usageHeaders; -#pragma warning restore IDE0044 // Add readonly modifier -#pragma warning disable IDE0044 // Add readonly modifier private Dictionary quotaHeaders; -#pragma warning restore IDE0044 // Add readonly modifier /// /// Constructor exposed for mocking purposes for the Azure Cosmos DB service. diff --git a/Microsoft.Azure.Cosmos/src/direct/ResourceType.cs b/Microsoft.Azure.Cosmos/src/direct/ResourceType.cs index 026a9165d4..36c577e933 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ResourceType.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ResourceType.cs @@ -82,6 +82,11 @@ internal enum ResourceType : int EncryptionScope = 156, + AzureRbac = 167, + + // Distributed transaction resource type + DistributedTransactionBatch = 168, + // These names make it unclear what they map to in ResourceType. Key = -2, Media = -3, @@ -95,9 +100,7 @@ internal enum ResourceType : int internal static class ResourceTypeExtensions { -#pragma warning disable IDE0044 // Add readonly modifier private static Dictionary resourceTypeNames = new Dictionary(); -#pragma warning restore IDE0044 // Add readonly modifier static ResourceTypeExtensions() { diff --git a/Microsoft.Azure.Cosmos/src/direct/RntbdConstants.cs b/Microsoft.Azure.Cosmos/src/direct/RntbdConstants.cs index 7e41fe48ce..d0ce245296 100644 --- a/Microsoft.Azure.Cosmos/src/direct/RntbdConstants.cs +++ b/Microsoft.Azure.Cosmos/src/direct/RntbdConstants.cs @@ -68,6 +68,11 @@ public enum RntbdResourceType : ushort StorageAuthToken = 0x002D, RetriableWriteCachedResponse = 0x002E, EncryptionScope = 0x0030, + AzureRbac = 0x0034, + DistributedTransactionBatch = 0x0035, + + // Please update RntbdConstants.tt T4 template and generate + // Also get sign-off from cdbcosdk } public enum RntbdOperationType : ushort @@ -127,6 +132,20 @@ public enum RntbdOperationType : ushort CreateRidRangeResources = 0x0036, Truncate = 0x0037, RelocateLeakedTentativeWrites = 0x0039, + ExternalPreBackup = 0x003A, + ExternalBackup = 0x003B, + CheckExternalBackupStatus = 0x003C, + ExternalBackupRestore = 0x003D, + CheckExternalBackupRestoreStatus = 0x003E, + PrepareDistributedTransaction = 0x003F, + CommitDistributedTransaction = 0x0040, + AbortDistributedTransaction = 0x0041, + QueryPlan = 0x0042, + CancelExternalBackup = 0x0043, + CancelExternalBackupRestore = 0x0044, + + // Please update RntbdConstants.tt T4 template and generate + // Also get sign-off from cdbcosdk } public enum ConnectionContextRequestTokenIdentifiers : ushort @@ -138,6 +157,10 @@ public enum ConnectionContextRequestTokenIdentifiers : ushort EnableChannelMultiplexing = 0x0004, ThinProxySignature = 0x0005, ThinProxySignatureDate = 0x0006, + MutualTlsAuthMode = 0x0007, + + // Please update RntbdConstants.tt T4 template and generate + // Also get sign-off from cdbcosdk } public sealed class ConnectionContextRequest : RntbdTokenStream @@ -151,6 +174,10 @@ public sealed class ConnectionContextRequest : RntbdTokenStream @@ -195,6 +228,7 @@ public sealed class ConnectionContextResponse : RntbdTokenStream @@ -749,6 +858,29 @@ public sealed class Request : RntbdTokenStream public RntbdToken populateVectorIndexAggregateProgress; public RntbdToken allowTopologyUpsertWithoutIntent; public RntbdToken readGlobalCommittedData; + public RntbdToken isSoftDeletionOrRecoveryOperation; + public RntbdToken workloadId; + public RntbdToken regionalDatabaseAccountName; + public RntbdToken originalAuthTokenType; + public RntbdToken allowBarrierRequestWhenRWStatusRevoked; + public RntbdToken populateGlobalEpochRecordCount; + public RntbdToken populateCanFailoverManagerAccessDocumentStore; + public RntbdToken isEmbeddingGeneratorRequest; + public RntbdToken populateGloballyAcceptedFailoverPolicy; + public RntbdToken shouldProcessOnlyInHubRegion; + public RntbdToken bypassSoftDeletionBlocking; + public RntbdToken isStrongConsistencyStoreClient; + public RntbdToken azureRbacName; + public RntbdToken populateGlobalStateWriteQuorumRegionsSet; + public RntbdToken isRequestIgnoredForAutoscaleReporting; + public RntbdToken dtcVersion; + public RntbdToken userAgent; + public RntbdToken contentType; + public RntbdToken idempotencyToken; + public RntbdToken allowInternalServerlessOfferRead; + public RntbdToken hybridLogicalClockTimestamp; + public RntbdToken distributedTransactionId; + public RntbdToken createPKRangesWithStatusOffline; public Request() { @@ -965,6 +1097,29 @@ public Request() this.populateVectorIndexAggregateProgress = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.PopulateVectorIndexAggregateProgress); this.allowTopologyUpsertWithoutIntent = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.AllowTopologyUpsertWithoutIntent); this.readGlobalCommittedData = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.ReadGlobalCommittedData); + this.isSoftDeletionOrRecoveryOperation = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.IsSoftDeletionOrRecoveryOperation); + this.workloadId = new RntbdToken(false, RntbdTokenTypes.UShort, (ushort)RequestIdentifiers.WorkloadId); + this.regionalDatabaseAccountName = new RntbdToken(false, RntbdTokenTypes.String, (ushort)RequestIdentifiers.RegionalDatabaseAccountName); + this.originalAuthTokenType = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.OriginalAuthTokenType); + this.allowBarrierRequestWhenRWStatusRevoked = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.AllowBarrierRequestWhenRWStatusRevoked); + this.populateGlobalEpochRecordCount = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.PopulateGlobalEpochRecordCount); + this.populateCanFailoverManagerAccessDocumentStore = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.PopulateCanFailoverManagerAccessDocumentStore); + this.isEmbeddingGeneratorRequest = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.IsEmbeddingGeneratorRequest); + this.populateGloballyAcceptedFailoverPolicy = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.PopulateGloballyAcceptedFailoverPolicy); + this.shouldProcessOnlyInHubRegion = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.ShouldProcessOnlyInHubRegion); + this.bypassSoftDeletionBlocking = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.BypassSoftDeletionBlocking); + this.isStrongConsistencyStoreClient = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.IsStrongConsistencyStoreClient); + this.azureRbacName = new RntbdToken(false, RntbdTokenTypes.String, (ushort)RequestIdentifiers.AzureRbacName); + this.populateGlobalStateWriteQuorumRegionsSet = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.PopulateGlobalStateWriteQuorumRegionsSet); + this.isRequestIgnoredForAutoscaleReporting = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.IsRequestIgnoredForAutoscaleReporting); + this.dtcVersion = new RntbdToken(false, RntbdTokenTypes.String, (ushort)RequestIdentifiers.DtcVersion); + this.userAgent = new RntbdToken(false, RntbdTokenTypes.String, (ushort)RequestIdentifiers.UserAgent); + this.contentType = new RntbdToken(false, RntbdTokenTypes.String, (ushort)RequestIdentifiers.ContentType); + this.idempotencyToken = new RntbdToken(false, RntbdTokenTypes.Guid, (ushort)RequestIdentifiers.IdempotencyToken); + this.allowInternalServerlessOfferRead = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.AllowInternalServerlessOfferRead); + this.hybridLogicalClockTimestamp = new RntbdToken(false, RntbdTokenTypes.LongLong, (ushort)RequestIdentifiers.HybridLogicalClockTimestamp); + this.distributedTransactionId = new RntbdToken(false, RntbdTokenTypes.Guid, (ushort)RequestIdentifiers.DistributedTransactionId); + this.createPKRangesWithStatusOffline = new RntbdToken(false, RntbdTokenTypes.Byte, (ushort)RequestIdentifiers.CreatePKRangesWithStatusOffline); this.tokens = new RntbdToken[] { @@ -1198,6 +1353,29 @@ public Request() this.populateVectorIndexAggregateProgress, this.allowTopologyUpsertWithoutIntent, this.readGlobalCommittedData, + this.isSoftDeletionOrRecoveryOperation, + this.workloadId, + this.regionalDatabaseAccountName, + this.originalAuthTokenType, + this.allowBarrierRequestWhenRWStatusRevoked, + this.populateGlobalEpochRecordCount, + this.populateCanFailoverManagerAccessDocumentStore, + this.isEmbeddingGeneratorRequest, + this.populateGloballyAcceptedFailoverPolicy, + this.shouldProcessOnlyInHubRegion, + this.bypassSoftDeletionBlocking, + this.isStrongConsistencyStoreClient, + this.azureRbacName, + this.populateGlobalStateWriteQuorumRegionsSet, + this.isRequestIgnoredForAutoscaleReporting, + this.dtcVersion, + this.userAgent, + this.contentType, + this.idempotencyToken, + this.allowInternalServerlessOfferRead, + this.hybridLogicalClockTimestamp, + this.distributedTransactionId, + this.createPKRangesWithStatusOffline, }; } } @@ -1302,6 +1480,10 @@ public enum ResponseIdentifiers : ushort VectorIndexAggregateProgress = 0x0083, MergeProgressBlockedReason = 0x0084, ThroughputBucketApplied = 0x0085, + HybridLogicalClockTimestamp = 0x0086, + + // Please update RntbdConstants.tt T4 template and generate + // Also get sign-off from cdbcosdk } // @@ -1333,6 +1515,9 @@ public enum CallerId : byte /// Invalid caller Id /// Invalid = 0x04, + + // Please update RntbdConstants.tt T4 template and generate + // Also get sign-off from cdbcosdk } internal sealed class RntbdEntityPool diff --git a/Microsoft.Azure.Cosmos/src/direct/RntbdToken.cs b/Microsoft.Azure.Cosmos/src/direct/RntbdToken.cs index b18e45764d..e2da4ecf2e 100644 --- a/Microsoft.Azure.Cosmos/src/direct/RntbdToken.cs +++ b/Microsoft.Azure.Cosmos/src/direct/RntbdToken.cs @@ -84,15 +84,9 @@ internal struct RntbdTokenValue internal sealed class RntbdToken { -#pragma warning disable IDE0044 // Add readonly modifier private ushort identifier; -#pragma warning restore IDE0044 // Add readonly modifier -#pragma warning disable IDE0044 // Add readonly modifier private RntbdTokenTypes type; -#pragma warning restore IDE0044 // Add readonly modifier -#pragma warning disable IDE0044 // Add readonly modifier private bool isRequired; -#pragma warning restore IDE0044 // Add readonly modifier public bool isPresent; public RntbdTokenValue value; diff --git a/Microsoft.Azure.Cosmos/src/direct/RntbdTokenStream.cs b/Microsoft.Azure.Cosmos/src/direct/RntbdTokenStream.cs index b4852849a8..74c74c3a1d 100644 --- a/Microsoft.Azure.Cosmos/src/direct/RntbdTokenStream.cs +++ b/Microsoft.Azure.Cosmos/src/direct/RntbdTokenStream.cs @@ -25,19 +25,13 @@ internal abstract class RntbdTokenStream // System.Text.Encoding and Write/Read for stream don't take Memory<> in NetStandard // so we have to use ArrayPool instead. #if COSMOSCLIENT -#pragma warning disable IDE0044 // Add readonly modifier private ArrayPool arrayPool = ArrayPool.Create(); -#pragma warning restore IDE0044 // Add readonly modifier -#pragma warning disable IDE0044 // Add readonly modifier private List borrowedBytes = new List(); -#pragma warning restore IDE0044 // Add readonly modifier #endif public abstract int RequiredTokenCount { get; } - -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -/// + /// /// Gets a byte[] of at least bytes from a pool. /// /// The length of bytes to retrieve @@ -46,7 +40,6 @@ internal abstract class RntbdTokenStream /// Typically this is done when the request is returned to a shared pool of RNTBD requests. /// public byte[] GetBytes(int length) -#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved { #if COSMOSCLIENT byte[] bytes = this.arrayPool.Rent(length); diff --git a/Microsoft.Azure.Cosmos/src/direct/SDKSupportedCapabilities.cs b/Microsoft.Azure.Cosmos/src/direct/SDKSupportedCapabilities.cs index 557aba0032..b77d7480f2 100644 --- a/Microsoft.Azure.Cosmos/src/direct/SDKSupportedCapabilities.cs +++ b/Microsoft.Azure.Cosmos/src/direct/SDKSupportedCapabilities.cs @@ -5,6 +5,7 @@ namespace Microsoft.Azure.Documents { using System; + // NOTE: When modifying this enum, also update the corresponding SDKSupportedCapabilities.h file [Flags] internal enum SDKSupportedCapabilities : ulong { @@ -12,6 +13,7 @@ internal enum SDKSupportedCapabilities : ulong PartitionMerge = 1 << 0, ChangeFeedWithStartTimePostMerge = 1 << 1, ThroughputBucketing = 1 << 2, - IgnoreUnknownRntbdTokens = 1 << 3 + IgnoreUnknownRntbdTokens = 1 << 3, + ChangeFeedTokenWithGCN = 1 << 4 } } diff --git a/Microsoft.Azure.Cosmos/src/direct/SerializableNameValueCollection.cs b/Microsoft.Azure.Cosmos/src/direct/SerializableNameValueCollection.cs index 6730c27a1d..cb44ddd5b0 100644 --- a/Microsoft.Azure.Cosmos/src/direct/SerializableNameValueCollection.cs +++ b/Microsoft.Azure.Cosmos/src/direct/SerializableNameValueCollection.cs @@ -13,9 +13,7 @@ namespace Microsoft.Azure.Documents internal sealed class SerializableNameValueCollection : JsonSerializable { -#pragma warning disable IDE0044 // Add readonly modifier private Lazy lazyCollection; -#pragma warning restore IDE0044 // Add readonly modifier public SerializableNameValueCollection() { diff --git a/Microsoft.Azure.Cosmos/src/direct/ServiceIdentity.cs b/Microsoft.Azure.Cosmos/src/direct/ServiceIdentity.cs index 4fe68c876a..8b2d501dc0 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ServiceIdentity.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ServiceIdentity.cs @@ -17,7 +17,6 @@ internal interface IServiceIdentity internal sealed class ServiceIdentity : IServiceIdentity { -#pragma warning disable CS1587 [JsonConstructor] /// /// Needed for TestForWhiteListedPersistedTypes to succeed @@ -25,7 +24,6 @@ internal sealed class ServiceIdentity : IServiceIdentity private ServiceIdentity() { } -#pragma warning restore CS1587 public ServiceIdentity(string federationId, Uri serviceName, bool isMasterService) { diff --git a/Microsoft.Azure.Cosmos/src/direct/ServiceUnavailableException.cs b/Microsoft.Azure.Cosmos/src/direct/ServiceUnavailableException.cs index 54c788c089..f6b00721c9 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ServiceUnavailableException.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ServiceUnavailableException.cs @@ -125,6 +125,8 @@ private static string GetExceptionMessage(SubStatusCodes? subStatusCode) return RMResources.Server_NoValidStoreResponse; case SubStatusCodes.Channel_Closed: return RMResources.ChannelClosed; + case SubStatusCodes.Server_NRegionCommitWriteBarrierNotMet: + return RMResources.Server_NRegionCommitWriteBarrierNotMet; default: return RMResources.ServiceUnavailable; } diff --git a/Microsoft.Azure.Cosmos/src/direct/SessionTokenMismatchRetryPolicy.cs b/Microsoft.Azure.Cosmos/src/direct/SessionTokenMismatchRetryPolicy.cs index a73b8130d6..ab26bd572d 100644 --- a/Microsoft.Azure.Cosmos/src/direct/SessionTokenMismatchRetryPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/direct/SessionTokenMismatchRetryPolicy.cs @@ -26,12 +26,8 @@ internal sealed class SessionTokenMismatchRetryPolicy : IRetryPolicy, IRequestRe private static readonly Lazy sessionRetryMaximumBackoffConfig; private int retryCount; -#pragma warning disable IDE0044 // Add readonly modifier private Stopwatch durationTimer = new Stopwatch(); -#pragma warning restore IDE0044 // Add readonly modifier -#pragma warning disable IDE0044 // Add readonly modifier private int waitTimeInMilliSeconds; -#pragma warning restore IDE0044 // Add readonly modifier private int? currentBackoffInMilliSeconds; @@ -128,9 +124,7 @@ public bool TryHandleResponseSynchronously(DocumentServiceRequest request, return true; } -#pragma warning disable VSTHRD200 // Use "Async" suffix for async methods private ShouldRetryResult ShouldRetryInternalAsync(DocumentServiceRequest request, -#pragma warning restore VSTHRD200 // Use "Async" suffix for async methods HttpStatusCode? statusCode, SubStatusCodes? subStatusCode, long? responseLSN) @@ -139,7 +133,6 @@ private ShouldRetryResult ShouldRetryInternalAsync(DocumentServiceRequest reques if (statusCode.HasValue && statusCode.Value == HttpStatusCode.NotFound && subStatusCode.HasValue && subStatusCode.Value == SubStatusCodes.ReadSessionNotAvailable) -#pragma warning disable SA1505 // Opening braces should not be followed by blank line { int remainingTimeInMilliSeconds = this.waitTimeInMilliSeconds - Convert.ToInt32(this.durationTimer.Elapsed.TotalMilliseconds); @@ -148,12 +141,10 @@ private ShouldRetryResult ShouldRetryInternalAsync(DocumentServiceRequest reques { this.durationTimer.Stop(); -#pragma warning disable SA1003 // Symbols should be spaced correctly DefaultTrace.TraceInformation("SessionTokenMismatchRetryPolicy not retrying because it has exceeded the time limit. Retry count = {0} request-session-token = {1} response-session-token = {2}", this.retryCount, requestSessionToken == null ? "" : requestSessionToken.ConvertToString(), responseLSN.HasValue? responseLSN : ""); -#pragma warning restore SA1003 // Symbols should be spaced correctly return ShouldRetryResult.NoRetry(); } @@ -209,7 +200,6 @@ private ShouldRetryResult ShouldRetryInternalAsync(DocumentServiceRequest reques return ShouldRetryResult.RetryAfter(backoffTime); } -#pragma warning restore SA1505 // Opening braces should not be followed by blank line this.durationTimer.Stop(); @@ -218,11 +208,24 @@ private ShouldRetryResult ShouldRetryInternalAsync(DocumentServiceRequest reques private bool shouldRetryLocally() { - if (this.sessionRetryOptions == null || !this.sessionRetryOptions.RemoteRegionPreferred) + // If no options, allow retry (legacy behavior) + if (this.sessionRetryOptions == null) { return true; } + // If remote region is not preferred, use legacy retry logic + if (!this.sessionRetryOptions.RemoteRegionPreferred) + { + return true; + } + + // If retries are disabled, do not retry + if (this.sessionRetryOptions.MaxInRegionRetryCount <= 0) + { + return false; + } + // SessionTokenMismatchRetryPolicy is invoked after 1 attempt on a region // sessionTokenMismatchRetryAttempts increments only after shouldRetry triggers // another attempt on the same region diff --git a/Microsoft.Azure.Cosmos/src/direct/Snapshot.cs b/Microsoft.Azure.Cosmos/src/direct/Snapshot.cs index 463184d22b..9acd233524 100644 --- a/Microsoft.Azure.Cosmos/src/direct/Snapshot.cs +++ b/Microsoft.Azure.Cosmos/src/direct/Snapshot.cs @@ -41,11 +41,8 @@ namespace Microsoft.Azure.Documents #endif class Snapshot : Resource { -#pragma warning disable CS0108 -#pragma warning disable IDE0044 - private static DateTime UnixStartTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); -#pragma warning restore CS0108 -#pragma warning restore IDE0044 + private static new DateTime UnixStartTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + private SnapshotContent snapshotContent; /// diff --git a/Microsoft.Azure.Cosmos/src/direct/SoftDeletionMetadata.cs b/Microsoft.Azure.Cosmos/src/direct/SoftDeletionMetadata.cs index 79d21d45b5..cdaf9bd110 100644 --- a/Microsoft.Azure.Cosmos/src/direct/SoftDeletionMetadata.cs +++ b/Microsoft.Azure.Cosmos/src/direct/SoftDeletionMetadata.cs @@ -32,42 +32,47 @@ public bool IsSoftDeleted this.SetValue(Constants.SoftDeletionMetadataProperties.IsSoftDeleted, value); } } -#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row /// - /// Property to indicate Database Account Soft Deletion Start Timestamp. + /// Property to indicate Database Account Soft Deletion Start Timestamp in epoch format. /// - [JsonConverter(typeof(IsoDateTimeConverter))] -#pragma warning restore SA1507 // Code should not contain multiple blank lines in a row - [JsonProperty(PropertyName = Constants.SoftDeletionMetadataProperties.SoftDeletionStartTimestampUtc)] - public DateTime SoftDeletionStartTimestampUtc + [JsonProperty(PropertyName = Constants.SoftDeletionMetadataProperties.SoftDeletionStartTimestamp)] + public long SoftDeletionStartTimestamp { get { - return base.GetValue(Constants.SoftDeletionMetadataProperties.SoftDeletionStartTimestampUtc).ToUniversalTime(); + return base.GetValue(Constants.SoftDeletionMetadataProperties.SoftDeletionStartTimestamp); } set { - this.SetValue(Constants.SoftDeletionMetadataProperties.SoftDeletionStartTimestampUtc, value.ToUniversalTime()); + this.SetValue(Constants.SoftDeletionMetadataProperties.SoftDeletionStartTimestamp, value); } } /// - /// Property to indicate Database Account Soft Deletion Expiration Timestamp. + /// Property to indicate Database Account Soft Deletion Expiration Timestamp in epoch format. /// - [JsonConverter(typeof(IsoDateTimeConverter))] - [JsonProperty(PropertyName = Constants.SoftDeletionMetadataProperties.SoftDeletionResourceExpirationTimestampUtc)] - public DateTime SoftDeletionResourceExpirationTimestampUtc + [JsonProperty(PropertyName = Constants.SoftDeletionMetadataProperties.SoftDeletionResourceExpirationTimestamp)] + public long SoftDeletionResourceExpirationTimestamp { get { - return base.GetValue(Constants.SoftDeletionMetadataProperties.SoftDeletionResourceExpirationTimestampUtc).ToUniversalTime(); + return base.GetValue(Constants.SoftDeletionMetadataProperties.SoftDeletionResourceExpirationTimestamp); } set { - this.SetValue(Constants.SoftDeletionMetadataProperties.SoftDeletionResourceExpirationTimestampUtc, value.ToUniversalTime()); + this.SetValue(Constants.SoftDeletionMetadataProperties.SoftDeletionResourceExpirationTimestamp, value); } } + + public override string ToString() + { + return string.Format( + "IsSoftDeleted: {0}, SoftDeletionStartTimestamp: {1}, SoftDeletionResourceExpirationTimestamp: {2}", + this.IsSoftDeleted, + this.SoftDeletionStartTimestamp, + this.SoftDeletionResourceExpirationTimestamp); + } } } diff --git a/Microsoft.Azure.Cosmos/src/direct/StatusCodes.cs b/Microsoft.Azure.Cosmos/src/direct/StatusCodes.cs index 8dc8b460dc..07176f011a 100644 --- a/Microsoft.Azure.Cosmos/src/direct/StatusCodes.cs +++ b/Microsoft.Azure.Cosmos/src/direct/StatusCodes.cs @@ -40,9 +40,10 @@ internal enum StatusCodes BadGateway = 502, ServiceUnavailable = 503, - //Operation pause and cancel. These are FAKE status codes for QOS logging purpose only. + //Operation pause, cancel, and yield. These are FAKE status codes for QOS logging purpose only. OperationPaused = 1200, OperationCancelled = 1201, + OperationYielding = 1202, // Internal Status Codes for Migration errors PartitionMigrationOperationException = 2001 @@ -73,11 +74,6 @@ internal enum SubStatusCodes ScriptCompileError = 0xFFFF, // From ExecuteStoredProcedure. AnotherOfferReplaceOperationIsInProgress = 3205, HttpListenerException = 1101, - NotImplementedPartitonKeyKindOrVersion = 1038, // Used for ThinProxy - MultipleAccountsNotAllowedInSameConnection = 1039, // Used for ThinProxy - ThinClientPublicEndpointDisabled = 1040, // Used for ThinProxy - MissingRequiredRntbdToken = 1041, // Used for ThinProxy - InvalidRntbdToken = 1042, // Used for ThinProxy // 410: StatusCodeType_Gone: substatus NameCacheIsStale = 1000, @@ -110,6 +106,7 @@ internal enum SubStatusCodes RedundantCollectionPut = 1009, SharedThroughputDatabaseQuotaExceeded = 1010, SharedThroughputOfferGrowNotNeeded = 1011, + RedundantDatabasePut = 1012, PartitionKeyQuotaOverLimit = 1014, SharedThroughputDatabaseCollectionCountExceeded = 1019, SharedThroughputDatabaseCountExceeded = 1020, @@ -126,6 +123,30 @@ internal enum SubStatusCodes // 409: Partition migration Count mismatch conflict sub status codes PartitionMigrationDocumentCountMismatchBetweenSourceAndTargetPartition = 3050, PartitionMigrationDocumentCountMismatchBetweenTargetPartitionReplicas = 3051, + PartitionMigrationDocumentCountMismatchWithinPartitionReplicas = 3052, + + // Error codes for external backup and restore, ranges from 3500-3599 + InvalidBackupMetadataInRestoreRequestClientError = 3500, + IncorrectApiTypeForCosmosDBAccountUserError = 3501, + RequestNotSupportedOnMultiRegionCosmosDBAccountUserError = 3502, + RequestNotSupportedOnOfflineCosmosDBAccountUserError = 3503, + RequestNotSupportedOnNonEmptyCosmosDBAccountUserError = 3504, + MissingMetadataStorageUnitInRestoreRequestClientError = 3505, + MissingDataStorageUnitInRestoreRequestClientError = 3506, + ExternalBackupAndRestoreServerError = 3507, + AnotherOperationInProgressOnCosmosDBAccountUserError = 3508, + InvalidMetadataCommitBlobUriClientError = 3509, + CrossRegionExternalBackupNotAllowedUserError = 3510, + PartitionLimitExceededUserError = 3511, + BackupNotReadyServerError = 3512, + InvalidBackupTimestampClientError = 3513, + LongTermProtectionNotEnabledUserError = 3514, + RequestNotSupportedOnPeriodicBackupModeCosmosDBAccountUserError = 3515, + RequestNotSupportedOnPPAFEnabledCosmosDBAccountUserError = 3516, + CrossRegionLongTermProtectionNotAllowedUserError = 3517, + RestoreNotSupportedOnServerlessCosmosDBAccountUserError = 3518, + IncrementalBackupNotYetSupportedUserError = 3519, + IncrementalBackupRestoreNotYetSupportedUserError = 3520, // 503: Service Unavailable due to region being out of capacity for bindable partitions InsufficientBindablePartitions = 1007, @@ -210,7 +231,7 @@ internal enum SubStatusCodes PartitionMigrationIsDisabledAtTheCurrentArmLocation = 2054, PartitionMigrationFailedToAcquirePartitionMigrationLocks = 2055, PartitionMigrationIsDisabledOnTheRegionalAccount = 2056, - PartitionMigrationWaitForCatchupTimedOut = 2057, + PartitionMigrationWaitForLSNCatchupTimedOut = 2057, PartitionMigrationFailureMitigationWrongServiceType = 2058, PartitionMigrationWaitForTargetReplicaResourceCreationTimedOut = 2059, PartitionMigrationIsDisabledOnTheService = 2060, @@ -225,11 +246,48 @@ internal enum SubStatusCodes PartitionMigrationCanNotProceedDuringBlockedWindow = 2069, RevokedPartitionMigrationCanNotProceedForMultimasterAccount = 2070, RevokedPartitionMigrationCanNotProceedForNonRevokedAccount = 2071, - PartitionMigrationWaitForCatchupGotCancelledBeforeTimeout = 2072, + PartitionMigrationWaitForLSNCatchupGotCancelledBeforeTimeout = 2072, PartitionMigrationWaitForLocalCatchupTimedOut = 2073, PartitionMigrationFailureMitigationInvalidResumeStep = 2074, PartitionMigrationResetConfigsAfterWaitForFullSyncTookTooLong = 2075, PartitionMigrationResetConfigsAfterWaitForFullSyncReceivedException = 2076, + PartitionMigrationAccountConsistencyLevelIsBoundedStaleness = 2077, + PartitionMigrationWaitForVectorIndexCatchupTimedOut = 2078, + PartitionMigrationWaitForVectorIndexingCatchupGotCancelledBeforeTimeout = 2079, + PartitionMigrationWaitForCatchupFailedWithException = 2080, + PartitionMigrationListingMasterServicePartitionsFailed = 2081, + PartitionMigrationListingServerPartitionsFromMasterFailed = 2082, + PartitionMigrationCanNotAcquireRegionalPartitionMigrationLock = 2083, + PartitionMigrationDidNotCompleteValidateDocumentCountAcrossReplicasInTenRetries = 2084, + PartitionMigrationCanNotProceedForFailoverFailedRegionalDatabaseAccount = 2085, + PartitionMigrationFailedWithExceptionInjection = 2086, + PartitionMigrationTargetInFederationsToAvoid = 2087, + RevokedPartitionMigrationCanNotAcquireDatabaseAccountLock = 2088, + RevokedPartitionReseedingCannotProceedForNonRevokedAccount = 2089, + RevokedPartitionReseedingIsDisabled = 2090, + RevokedPartitionMigrationCanNotAcquireGlobalDatabaseAccountLock = 2091, + RevokedPartitionReseedingCanNotAcquireGlobalDatabaseAccountLock = 2092, + PartitionMigrationFailureMitigationTargetNotBound = 2093, + PartitionMigrationAttemptedBeforeCooldown = 2094, + PartitionMigrationSourceServiceDeleted = 2095, + + // 412: PreconditionFailed codes for PartitionMigration from backend + MismatchingCollectionRidsOnMigratePartitionDuringMigration = 5325, + + // 412: PreConditionFailed Cross SubRegion Migration SubStatus Codes + CrossSubRegionMigrationIsDisabledOnTheRegionalAccount = 2200, + + // 412: PreConditionFailed SoftDelete substatus codes + AccountIsSoftDeleted = 1300, + DatabaseIsSoftDeleted = 1301, + CollectionIsSoftDeleted = 1302, + ResourceStillSoftDeleted = 1303, + ServerSoftDeletionMetadataOutOfSyncWithMaster = 1304, + SoftDeletionTimestampsNotUpdated = 1305, + SoftDeletionMetadataMismatch = 1306, + SoftDeleteOperationFailedWithExceptionInjection = 1307, + NullSoftDeletionMetadata = 1308, + InvalidSoftDeletionMetadata = 1309, // 500: InternalServerError ConfigurationNameNotEmpty = 3001, @@ -239,6 +297,7 @@ internal enum SubStatusCodes PartitionFailoverErrorCode = 3010, OperationManagerDequeuePumpStopped = 3021, OperationCancelledWithNoRollback = 3042, + SplitTimedOut = 3043, // 429: Request Rate Too Large PrepareTimeLimitExceeded = 3207, @@ -251,6 +310,7 @@ internal enum SubStatusCodes SystemResourceUnavailable = 3092, ThrottleDueToTransportBufferUsage = 3103, TooManyThroughputBucketUpdates = 3213, + MicrosoftFabricCUBudgetExceeded = 3300, // Key Vault Access Client Error Code AadClientCredentialsGrantFailure = 4000, // Indicated access to AAD failed to get a token @@ -274,6 +334,16 @@ internal enum SubStatusCodes KeyDisabledOrExpired = 4018, // Indicates that the Key Vault key has been disabled. MasterServiceUnavailable = 4019, // Indicates that the master service is unavailable. + // Fabric codes + // Fabric codes are in the range of 6050-6999 + InsufficientFabricPermissions = 6050, + FabricAuthorizationFailed = 6051, + FabricOperationUnsupported = 6052, + FabricTokenValidatonFailed = 6053, + InvalidFabricAppId = 6054, + InvalidFabricTenantId = 6055, + InvalidFabricAritfactId = 6056, + // Keep in sync with Microsoft.Azure.Cosmos.ServiceFramework.Security.AadAuthentication.AadSubStatusCodes // 401 : Unauthorized Exception (User-side errors start with 50) MissingAuthHeader = 5000, @@ -341,6 +411,8 @@ internal enum SubStatusCodes IncrementGlobalConfigurationNumberTopologyUpsertIntentNotApplied = 8003, AddRegionUpsertIntentNotApplied = 8004, RemoveReadRegionUpsertIntentNotApplied = 8005, + RemoveReadRegionTopologyValidationFailed = 8006, + PrepareForEntityDeletionTopologyValidationFailed = 8007, // SDK Codes (Client) // IMPORTANT - keep these consistent with Java SDK as well @@ -363,6 +435,9 @@ internal enum SubStatusCodes // INVALID_BACKEND_RESPONSE = 20908; // UNKNOWN_QUORUM_RESULT = 20909; // INVALID_RESULT = 20910; + // TRANSIT_TIMEOUT = 20911; + // CLOSED_CLIENT = 20912; + WriteRegionBarrierChangedMidOperation = 20913, //SDK Codes (Server) // IMPORTANT - keep these consistent with Java SDK as well @@ -376,10 +451,36 @@ internal enum SubStatusCodes ServerGenerated503 = 21008, Server_NoValidStoreResponse = 21009, // ServerGenerated408 = 21010 - currently only applicable in Java + Server_BarrierThrottled = 21011, // indicates the primary operation succeeded, but barrier failed due to 429 throttling + Server_NRegionCommitWriteBarrierNotMet = 21012, + Server_WriteBarrierThrottled = 21013, // Data Transfer Application related MissingPartitionKeyInDataTransfer = 22001, - InvalidPartitionKeyInDataTransfer = 22002 + InvalidPartitionKeyInDataTransfer = 22002, + + // Failover Priority Change Rollback Failed + FailoverPriorityChangeRollbackFailed = 23001, + + #region ThinProxy specific subStatusCodes + // ThinProxy specific subStatusCodes are in the range of 13000-13100 + ThinProxy_MultipleAccountsNotAllowedInSameConnection = 13000, + ThinProxy_ThinClientPublicEndpointDisabled = 13001, + ThinProxy_MissingRequiredRntbdToken = 13002, + ThinProxy_InvalidRntbdToken = 13003, + ThinProxy_DefaultConsistencyLevelIsNull = 13004, + ThinProxy_InvalidRequestLevelConsistency = 13005, + ThinProxy_TopologyClientBadRequest = 13006, + ThinProxy_InvalidRequestBytes = 13007, + ThinProxy_Generated401 = 13008, + ThinProxy_Generated408 = 13009, + ThinProxy_RequestThrottled = 13010, + ThinProxy_Generated500 = 13011, + ThinProxy_Generated503 = 13012, + ThinProxy_QueryPlanParsingError = 13013, + ThinProxy_QueryPlanCompilationError = 13014, + ThinProxy_QueryPlanUnexpectedError = 13015, + #endregion } internal static class StatusCodesExtensions @@ -424,6 +525,12 @@ public static bool IsSDKGeneratedSubStatus(this SubStatusCodes code) { return ((int)code > SubStatusCodesExtensions.SDKGeneratedSubStatusStartingCode); } + + public static bool IsExternalBackupAndRestoreSubStatus(this SubStatusCodes code) + { + int intCode = (int)code; + return (intCode >= 3500 && intCode <= 3599); + } } } diff --git a/Microsoft.Azure.Cosmos/src/direct/StoreClient.cs b/Microsoft.Azure.Cosmos/src/direct/StoreClient.cs index 5c8e0054ea..42657c69c0 100644 --- a/Microsoft.Azure.Cosmos/src/direct/StoreClient.cs +++ b/Microsoft.Azure.Cosmos/src/direct/StoreClient.cs @@ -42,7 +42,6 @@ public StoreClient( bool detectClientConnectivityIssues = false, bool disableRetryWithRetryPolicy = false, bool enableReplicaValidation = false, - AccountConfigurationProperties accountConfigurationProperties = null, RetryWithConfiguration retryWithConfiguration = null, ISessionRetryOptions sessionRetryOptions = null) { @@ -73,7 +72,6 @@ public StoreClient( disableRetryWithRetryPolicy: disableRetryWithRetryPolicy, retryWithConfiguration: retryWithConfiguration, enableReplicaValidation: enableReplicaValidation, - accountConfigurationProperties: accountConfigurationProperties, sessionRetryOptions: sessionRetryOptions); } diff --git a/Microsoft.Azure.Cosmos/src/direct/StoreClientFactory.cs b/Microsoft.Azure.Cosmos/src/direct/StoreClientFactory.cs index 1612f7969c..38e2a3d473 100644 --- a/Microsoft.Azure.Cosmos/src/direct/StoreClientFactory.cs +++ b/Microsoft.Azure.Cosmos/src/direct/StoreClientFactory.cs @@ -8,6 +8,7 @@ namespace Microsoft.Azure.Documents using System.Diagnostics; using System.Net.Security; using System.Threading.Tasks; + using System.Security.Cryptography.X509Certificates; using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Documents.Client; using Microsoft.Azure.Documents.FaultInjection; @@ -21,9 +22,7 @@ internal sealed class StoreClientFactory : IStoreClientFactory private readonly bool disableRetryWithRetryPolicy; private TransportClient transportClient; private TransportClient fallbackTransportClient; -#pragma warning disable IDE0044 // Add readonly modifier private IConnectionStateListener connectionStateListener; -#pragma warning restore IDE0044 // Add readonly modifier public StoreClientFactory( Protocol protocol, @@ -52,6 +51,8 @@ public StoreClientFactory( int rntbdMaxConcurrentOpeningConnectionCount = ushort.MaxValue, // Optional for Rntbd MemoryStreamPool memoryStreamPool = null, RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, + Func clientCertificateFunction = null, + Action clientCertificateFailureHandler = null, Func> dnsResolutionFunction = null, // optional override DistributedTracingOptions distributedTracingOptions = null, // Distributed Tracing Configuration IChaosInterceptor chaosInterceptor = null) // Fault Injection @@ -196,6 +197,11 @@ public StoreClientFactory( sendHangDetectionTimeSeconds = maxSendHangDetectionTimeSeconds; } + if (clientCertificateFunction is null && clientCertificateFailureHandler is not null) + { + DefaultTrace.TraceWarning("Passed a clientCertificateFailureHandler without a clientCertificateFunction."); + } + StoreClientFactory.ValidateRntbdMaxConcurrentOpeningConnectionCount(ref rntbdMaxConcurrentOpeningConnectionCount); this.transportClient = new Rntbd.TransportClient( @@ -221,6 +227,8 @@ public StoreClientFactory( MaxConcurrentOpeningConnectionCount = rntbdMaxConcurrentOpeningConnectionCount, MemoryStreamPool = memoryStreamPool, RemoteCertificateValidationCallback = remoteCertificateValidationCallback, + ClientCertificateFunction = clientCertificateFunction, + ClientCertificateFailureHandler = clientCertificateFailureHandler, DnsResolutionFunction = dnsResolutionFunction, DistributedTracingOptions = distributedTracingOptions }, @@ -249,6 +257,8 @@ public StoreClientFactory( MaxConcurrentOpeningConnectionCount = rntbdMaxConcurrentOpeningConnectionCount, MemoryStreamPool = memoryStreamPool, RemoteCertificateValidationCallback = remoteCertificateValidationCallback, + ClientCertificateFunction = clientCertificateFunction, + ClientCertificateFailureHandler = clientCertificateFailureHandler, DnsResolutionFunction = dnsResolutionFunction, DistributedTracingOptions = distributedTracingOptions }, @@ -316,7 +326,6 @@ public StoreClient CreateStoreClient( bool useMultipleWriteLocations = false, bool detectClientConnectivityIssues = false, bool enableReplicaValidation = false, - AccountConfigurationProperties accountConfigurationProperties = null, ISessionRetryOptions sessionRetryOptions = null) { this.ThrowIfDisposed(); @@ -337,7 +346,6 @@ public StoreClient CreateStoreClient( disableRetryWithRetryPolicy: this.disableRetryWithRetryPolicy, retryWithConfiguration: this.retryWithConfiguration, enableReplicaValidation: enableReplicaValidation, - accountConfigurationProperties: accountConfigurationProperties, sessionRetryOptions: sessionRetryOptions); } @@ -346,7 +354,6 @@ public StoreClient CreateStoreClient( sessionContainer: sessionContainer, serviceConfigurationReader: serviceConfigurationReader, userTokenProvider: authorizationTokenProvider, - accountConfigurationProperties: accountConfigurationProperties, protocol: this.protocol, transportClient: this.transportClient, enableRequestDiagnostics: enableRequestDiagnostics, diff --git a/Microsoft.Azure.Cosmos/src/direct/StoreReader.cs b/Microsoft.Azure.Cosmos/src/direct/StoreReader.cs index 53906b6e04..f5aca3fc7c 100644 --- a/Microsoft.Azure.Cosmos/src/direct/StoreReader.cs +++ b/Microsoft.Azure.Cosmos/src/direct/StoreReader.cs @@ -521,6 +521,9 @@ private async Task> GetResult(DocumentSe case OperationType.ExecuteJavaScript: #if !COSMOSCLIENT case OperationType.MetadataCheckAccess: + case OperationType.ExternalPreBackup: + case OperationType.CheckExternalBackupStatus: + case OperationType.CheckExternalBackupRestoreStatus: case OperationType.GetStorageAuthToken: #endif { diff --git a/Microsoft.Azure.Cosmos/src/direct/StoreResponse.cs b/Microsoft.Azure.Cosmos/src/direct/StoreResponse.cs index 65335f6970..da0158df68 100644 --- a/Microsoft.Azure.Cosmos/src/direct/StoreResponse.cs +++ b/Microsoft.Azure.Cosmos/src/direct/StoreResponse.cs @@ -101,6 +101,20 @@ public string Continuation } } + public string BackendActivityId + { + get + { + string value; + if (this.TryGetHeaderValue(WFConstants.BackendHeaders.ActivityId, out value)) + { + return value; + } + + return null; + } + } + private SubStatusCodes? subStatusCode; public SubStatusCodes SubStatusCode diff --git a/Microsoft.Azure.Cosmos/src/direct/StoreResponseNameValueCollection.cs b/Microsoft.Azure.Cosmos/src/direct/StoreResponseNameValueCollection.cs index f53334f851..8336f32d44 100644 --- a/Microsoft.Azure.Cosmos/src/direct/StoreResponseNameValueCollection.cs +++ b/Microsoft.Azure.Cosmos/src/direct/StoreResponseNameValueCollection.cs @@ -71,6 +71,7 @@ internal class StoreResponseNameValueCollection : INameValueCollection, IEnumera public string GlobalNRegionCommittedGLSN { get; set; } public string HasTentativeWrites { get; set; } public string HighestTentativeWriteLLSN { get; set; } + public string HybridLogicalClockTimestamp { get; set; } public string IndexingDirective { get; set; } public string IndexUtilization { get; set; } public string InstantScaleUpValue { get; set; } @@ -90,6 +91,8 @@ internal class StoreResponseNameValueCollection : INameValueCollection, IEnumera public string MinGLSNForDocumentOperations { get; set; } public string MinGLSNForTombstoneOperations { get; set; } public string MinimumRUsForOffer { get; set; } + public string MutualTlsStatus { get; set; } + public string MutualTlsThumbprint { get; set; } public string NumberOfReadRegions { get; set; } public string OfferReplacePending { get; set; } public string OfferReplacePendingForMerge { get; set; } @@ -209,6 +212,7 @@ public void Clear() this.GlobalNRegionCommittedGLSN = null; this.HasTentativeWrites = null; this.HighestTentativeWriteLLSN = null; + this.HybridLogicalClockTimestamp = null; this.IndexingDirective = null; this.IndexUtilization = null; this.InstantScaleUpValue = null; @@ -228,6 +232,8 @@ public void Clear() this.MinGLSNForDocumentOperations = null; this.MinGLSNForTombstoneOperations = null; this.MinimumRUsForOffer = null; + this.MutualTlsStatus = null; + this.MutualTlsThumbprint = null; this.NumberOfReadRegions = null; this.OfferReplacePending = null; this.OfferReplacePendingForMerge = null; @@ -319,6 +325,7 @@ public INameValueCollection Clone() GlobalNRegionCommittedGLSN = this.GlobalNRegionCommittedGLSN, HasTentativeWrites = this.HasTentativeWrites, HighestTentativeWriteLLSN = this.HighestTentativeWriteLLSN, + HybridLogicalClockTimestamp = this.HybridLogicalClockTimestamp, IndexingDirective = this.IndexingDirective, IndexUtilization = this.IndexUtilization, InstantScaleUpValue = this.InstantScaleUpValue, @@ -338,6 +345,8 @@ public INameValueCollection Clone() MinGLSNForDocumentOperations = this.MinGLSNForDocumentOperations, MinGLSNForTombstoneOperations = this.MinGLSNForTombstoneOperations, MinimumRUsForOffer = this.MinimumRUsForOffer, + MutualTlsStatus = this.MutualTlsStatus, + MutualTlsThumbprint = this.MutualTlsThumbprint, NumberOfReadRegions = this.NumberOfReadRegions, OfferReplacePending = this.OfferReplacePending, OfferReplacePendingForMerge = this.OfferReplacePendingForMerge, @@ -789,6 +798,18 @@ IEnumerator> IEnumerable(HttpConstants.HttpHeaders.ThroughputBucketApplied, this.ThroughputBucketApplied); } + if (this.MutualTlsStatus != null) + { + yield return new KeyValuePair(HttpConstants.HttpHeaders.MutualTlsStatus, this.MutualTlsStatus); + } + if (this.MutualTlsThumbprint != null) + { + yield return new KeyValuePair(HttpConstants.HttpHeaders.MutualTlsThumbprint, this.MutualTlsThumbprint); + } + if (this.HybridLogicalClockTimestamp != null) + { + yield return new KeyValuePair(HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, this.HybridLogicalClockTimestamp); + } if (this.lazyNotCommonHeaders != null) { @@ -1200,6 +1221,18 @@ public IEnumerable Keys() { yield return HttpConstants.HttpHeaders.ThroughputBucketApplied; } + if (this.MutualTlsStatus != null) + { + yield return HttpConstants.HttpHeaders.MutualTlsStatus; + } + if (this.MutualTlsThumbprint != null) + { + yield return HttpConstants.HttpHeaders.MutualTlsThumbprint; + } + if (this.HybridLogicalClockTimestamp != null) + { + yield return HttpConstants.HttpHeaders.HybridLogicalClockTimestamp; + } if (this.lazyNotCommonHeaders != null) { @@ -1610,6 +1643,18 @@ public NameValueCollection ToNameValueCollection() { this.nameValueCollection.Add(HttpConstants.HttpHeaders.ThroughputBucketApplied, this.ThroughputBucketApplied); } + if (this.MutualTlsStatus != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.MutualTlsStatus, this.MutualTlsStatus); + } + if (this.MutualTlsThumbprint != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.MutualTlsThumbprint, this.MutualTlsThumbprint); + } + if (this.HybridLogicalClockTimestamp != null) + { + this.nameValueCollection.Add(HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, this.HybridLogicalClockTimestamp); + } if(this.lazyNotCommonHeaders != null) { foreach (KeyValuePair keyValuePair in this.lazyNotCommonHeaders) @@ -1704,6 +1749,10 @@ public string Get(string key) { return this.LocalLSN; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.MutualTlsStatus, key)) + { + return this.MutualTlsStatus; + } if (string.Equals(HttpConstants.HttpHeaders.ActivityId, key, StringComparison.OrdinalIgnoreCase)) { return this.ActivityId; @@ -1714,6 +1763,11 @@ public string Get(string key) return this.LocalLSN; } + if (string.Equals(HttpConstants.HttpHeaders.MutualTlsStatus, key, StringComparison.OrdinalIgnoreCase)) + { + return this.MutualTlsStatus; + } + break; case 17: if (object.ReferenceEquals(HttpConstants.HttpHeaders.Continuation, key)) @@ -1819,6 +1873,13 @@ public string Get(string key) return this.ServerVersion; } + break; + case 20: + if (string.Equals(HttpConstants.HttpHeaders.MutualTlsThumbprint, key, StringComparison.OrdinalIgnoreCase)) + { + return this.MutualTlsThumbprint; + } + break; case 21: if (object.ReferenceEquals(HttpConstants.HttpHeaders.OwnerFullName, key)) @@ -2503,6 +2564,13 @@ public string Get(string key) return this.CollectionLazyIndexingProgress; } + break; + case 51: + if (string.Equals(HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, key, StringComparison.OrdinalIgnoreCase)) + { + return this.HybridLogicalClockTimestamp; + } + break; case 52: if (object.ReferenceEquals(WFConstants.BackendHeaders.UnflushedMergLogEntryCount, key)) @@ -2696,6 +2764,16 @@ public void UpdateHelper( this.LocalLSN = value; return; } + if (object.ReferenceEquals(HttpConstants.HttpHeaders.MutualTlsStatus, key)) + { + if (throwIfAlreadyExists && this.MutualTlsStatus != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.MutualTlsStatus = value; + return; + } if (string.Equals(HttpConstants.HttpHeaders.ActivityId, key, StringComparison.OrdinalIgnoreCase)) { if (throwIfAlreadyExists && this.ActivityId != null) @@ -2716,6 +2794,16 @@ public void UpdateHelper( this.LocalLSN = value; return; } + if (string.Equals(HttpConstants.HttpHeaders.MutualTlsStatus, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.MutualTlsStatus != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.MutualTlsStatus = value; + return; + } break; case 17: if (object.ReferenceEquals(HttpConstants.HttpHeaders.Continuation, key)) @@ -2943,6 +3031,18 @@ public void UpdateHelper( return; } break; + case 20: + if (string.Equals(HttpConstants.HttpHeaders.MutualTlsThumbprint, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.MutualTlsThumbprint != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.MutualTlsThumbprint = value; + return; + } + break; case 21: if (object.ReferenceEquals(HttpConstants.HttpHeaders.OwnerFullName, key)) { @@ -4393,6 +4493,18 @@ public void UpdateHelper( return; } break; + case 51: + if (string.Equals(HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, key, StringComparison.OrdinalIgnoreCase)) + { + if (throwIfAlreadyExists && this.HybridLogicalClockTimestamp != null) + { + throw new ArgumentException($"The {key} already exists in the collection"); + } + + this.HybridLogicalClockTimestamp = value; + return; + } + break; case 52: if (object.ReferenceEquals(WFConstants.BackendHeaders.UnflushedMergLogEntryCount, key)) { diff --git a/Microsoft.Azure.Cosmos/src/direct/StoreResult.cs b/Microsoft.Azure.Cosmos/src/direct/StoreResult.cs index 38fd910f36..0895ba0f63 100644 --- a/Microsoft.Azure.Cosmos/src/direct/StoreResult.cs +++ b/Microsoft.Azure.Cosmos/src/direct/StoreResult.cs @@ -18,9 +18,7 @@ internal sealed class StoreResult : IDisposable { private readonly StoreResponse storeResponse; -#pragma warning disable IDE0044 // Add readonly modifier private static bool UseSessionTokenHeader = VersionUtility.IsLaterThan(HttpConstants.Versions.CurrentVersion, HttpConstants.VersionDates.v2018_06_18); -#pragma warning restore IDE0044 // Add readonly modifier public static ReferenceCountedDisposable CreateStoreResult( StoreResponse storeResponse, @@ -44,6 +42,7 @@ public static ReferenceCountedDisposable CreateStoreResult( long globalCommittedLSN = -1; int numberOfReadRegions = -1; long itemLSN = -1; + long globalNRegionCommittedGLSN = -1; if (storeResponse.TryGetHeaderValue( useLocalLSNBasedHeaders ? WFConstants.BackendHeaders.QuorumAckedLocalLSN : WFConstants.BackendHeaders.QuorumAckedLSN, out headerValue)) @@ -97,6 +96,11 @@ public static ReferenceCountedDisposable CreateStoreResult( lsn = storeResponse.LSN; } + if (storeResponse.TryGetHeaderValue(WFConstants.BackendHeaders.GlobalNRegionCommittedGLSN, out headerValue)) + { + globalNRegionCommittedGLSN = long.Parse(headerValue, CultureInfo.InvariantCulture); + } + ISessionToken sessionToken = null; if (StoreResult.UseSessionTokenHeader) { @@ -136,7 +140,8 @@ public static ReferenceCountedDisposable CreateStoreResult( backendRequestDurationInMs: backendRequestDurationMilliseconds, retryAfterInMs: retryAfterInMs, transportRequestStats: storeResponse.TransportRequestStats, - replicaHealthStatuses: replicaHealthStatuses)); + replicaHealthStatuses: replicaHealthStatuses, + globalNRegionCommittedGLSN: globalNRegionCommittedGLSN)); } else { @@ -148,6 +153,7 @@ public static ReferenceCountedDisposable CreateStoreResult( int currentWriteQuorum = -1; long globalCommittedLSN = -1; int numberOfReadRegions = -1; + long globalNRegionCommittedGLSN = -1; string headerValue = documentClientException.Headers[useLocalLSNBasedHeaders ? WFConstants.BackendHeaders.QuorumAckedLocalLSN : WFConstants.BackendHeaders.QuorumAckedLSN]; if (!string.IsNullOrEmpty(headerValue)) { @@ -199,6 +205,12 @@ public static ReferenceCountedDisposable CreateStoreResult( lsn = documentClientException.LSN; } + headerValue = documentClientException.Headers[WFConstants.BackendHeaders.GlobalNRegionCommittedGLSN]; + if (!string.IsNullOrEmpty(headerValue)) + { + globalNRegionCommittedGLSN = long.Parse(headerValue, CultureInfo.InvariantCulture); + } + ISessionToken sessionToken = null; if (StoreResult.UseSessionTokenHeader) { @@ -237,7 +249,8 @@ public static ReferenceCountedDisposable CreateStoreResult( backendRequestDurationInMs: documentClientException.Headers[HttpConstants.HttpHeaders.BackendRequestDurationMilliseconds], retryAfterInMs: documentClientException.Headers[HttpConstants.HttpHeaders.RetryAfterInMilliseconds], transportRequestStats: documentClientException.TransportRequestStats, - replicaHealthStatuses: replicaHealthStatuses)); + replicaHealthStatuses: replicaHealthStatuses, + globalNRegionCommittedGLSN: globalNRegionCommittedGLSN)); } else { @@ -262,17 +275,18 @@ public static ReferenceCountedDisposable CreateStoreResult( backendRequestDurationInMs: null, retryAfterInMs: null, transportRequestStats: null, - replicaHealthStatuses: replicaHealthStatuses)); + replicaHealthStatuses: replicaHealthStatuses, + globalNRegionCommittedGLSN: -1)); } } } - public static ReferenceCountedDisposable CreateForTesting(StoreResponse storeResponse) + public static ReferenceCountedDisposable CreateForTesting(StoreResponse storeResponse, int numberOfReadRegions = 0) { return new ReferenceCountedDisposable( new StoreResult( storeResponse, exception: null, null, default, default, default, default, default, default, - default, default, default, default, default, default, default, default, default, default, default)); + default, default, numberOfReadRegions, default, default, default, default, default, default, default, default, default)); } public static ReferenceCountedDisposable CreateForTesting(TransportRequestStats transportRequestStats) @@ -303,7 +317,8 @@ public static ReferenceCountedDisposable CreateForTesting(Transport "http://storephysicaladdress-2s.com:Unknown", "http://storephysicaladdress-3s.com:Unhealthy", "http://storephysicaladdress-4s.com:Unknown" - })); + }, + globalNRegionCommittedGLSN: -1)); } public static ReferenceCountedDisposable CreateForTesting(string partitionKeyRangeId) @@ -328,7 +343,8 @@ public static ReferenceCountedDisposable CreateForTesting(string pa backendRequestDurationInMs: "10", retryAfterInMs: "20", transportRequestStats: new TransportRequestStats(), - replicaHealthStatuses: null)); + replicaHealthStatuses: null, + globalNRegionCommittedGLSN: -1)); } private StoreResult( @@ -351,7 +367,8 @@ private StoreResult( string backendRequestDurationInMs, string retryAfterInMs, TransportRequestStats transportRequestStats, - IEnumerable replicaHealthStatuses) + IEnumerable replicaHealthStatuses, + long globalNRegionCommittedGLSN) { if (storeResponse == null && exception == null) { @@ -379,6 +396,7 @@ private StoreResult( this.RetryAfterInMs = retryAfterInMs; this.TransportRequestStats = transportRequestStats; this.ReplicaHealthStatuses = replicaHealthStatuses; + this.GlobalNRegionCommittedGLSN = globalNRegionCommittedGLSN; this.StatusCode = (StatusCodes) (this.storeResponse != null ? this.storeResponse.StatusCode : ((this.Exception != null && this.Exception.StatusCode.HasValue) ? this.Exception.StatusCode : 0)); @@ -429,6 +447,8 @@ private StoreResult( public IEnumerable ReplicaHealthStatuses { get; private set; } + public long GlobalNRegionCommittedGLSN { get; private set; } + public DocumentClientException GetException() { if (this.Exception == null) @@ -469,7 +489,7 @@ public StoreResponse ToResponse(RequestChargeTracker requestChargeTracker = null if (!string.IsNullOrWhiteSpace(this.StorePhysicalAddress?.AbsoluteUri)) { (string partitionId, string replicaId) = GetPartitionIdReplicaIdFromAddress(StorePhysicalAddress.AbsoluteUri); - //System.Diagnostics.Debug.Assert(string.IsNullOrWhiteSpace(partitionId) || Guid.TryParse(partitionId, out _), $"partitionId is invalid. value:{partitionId}"); + System.Diagnostics.Debug.Assert(string.IsNullOrWhiteSpace(partitionId) || Guid.TryParse(partitionId, out _), $"partitionId is invalid. value:{partitionId}"); if (this.Exception != null) { this.Exception.Headers[HttpConstants.HttpHeaders.PartitionId] = partitionId; @@ -505,7 +525,7 @@ public void AppendToBuilder(StringBuilder stringBuilder) stringBuilder.AppendFormat( CultureInfo.InvariantCulture, "StorePhysicalAddress: {0}, LSN: {1}, GlobalCommittedLsn: {2}, PartitionKeyRangeId: {3}, IsValid: {4}, StatusCode: {5}, SubStatusCode: {6}, " + - "RequestCharge: {7}, ItemLSN: {8}, SessionToken: {9}, UsingLocalLSN: {10}, TransportException: {11}, BELatencyMs: {12}, ActivityId: {13}, RetryAfterInMs: {14}", + "RequestCharge: {7}, ItemLSN: {8}, SessionToken: {9}, UsingLocalLSN: {10}, TransportException: {11}, BELatencyMs: {12}, ActivityId: {13}, RetryAfterInMs: {14}, globalNRegionCommittedGLSN: {15}", this.StorePhysicalAddress, this.LSN, this.GlobalCommittedLSN, @@ -520,7 +540,8 @@ public void AppendToBuilder(StringBuilder stringBuilder) this.Exception?.InnerException is TransportException ? this.Exception.InnerException.Message : "null", this.BackendRequestDurationInMs, this.ActivityId, - this.RetryAfterInMs); + this.RetryAfterInMs, + this.GlobalNRegionCommittedGLSN); if (this.ReplicaHealthStatuses != null && this.ReplicaHealthStatuses.Any()) { diff --git a/Microsoft.Azure.Cosmos/src/direct/StringSyntaxAttribute.cs b/Microsoft.Azure.Cosmos/src/direct/StringSyntaxAttribute.cs index 098e13eb08..636ee1b64f 100644 --- a/Microsoft.Azure.Cosmos/src/direct/StringSyntaxAttribute.cs +++ b/Microsoft.Azure.Cosmos/src/direct/StringSyntaxAttribute.cs @@ -7,17 +7,12 @@ #pragma warning disable NamespaceMatchesFolderStructure // The namespace needs to be System.Diagnostics.CodeAnalysis. namespace System.Diagnostics.CodeAnalysis #pragma warning restore NamespaceMatchesFolderStructure -#pragma warning disable SA1505 // Opening braces should not be followed by blank line { - -#pragma warning disable CS1584 // XML comment has syntactically incorrect cref attribute -#pragma warning restore SA1505 // Opening braces should not be followed by blank line /// /// Partial copy of NET7's StringSyntaxAttribute. /// - /// + /// See https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/StringSyntaxAttribute.cs [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] -#pragma warning restore CS1584 // XML comment has syntactically incorrect cref attribute #if DOCDBCLIENT // We don't want to update the public API in the client internal #else diff --git a/Microsoft.Azure.Cosmos/src/direct/SystemDocumentType.cs b/Microsoft.Azure.Cosmos/src/direct/SystemDocumentType.cs index 05fa394115..eca787ca80 100644 --- a/Microsoft.Azure.Cosmos/src/direct/SystemDocumentType.cs +++ b/Microsoft.Azure.Cosmos/src/direct/SystemDocumentType.cs @@ -29,5 +29,29 @@ internal enum SystemDocumentType /// PartitionedSystemDocument /// MaterializedViewLeaseStoreInitDocument, + + /// + /// Set the SystemDocumentType as MaterializedViewStatusDocument + /// PartitionedSystemDocument + /// + MaterializedViewStatusDocument, + + /// + /// Set the SystemDocumentType as EmbeddingGeneratorLeaseDocument + /// Partitioned SystemDocument + /// + EmbeddingGeneratorLeaseDocument, + + /// + /// Set the SystemDocumentType as MaterializedViewBuilderOwnershipDocument + /// NonPartitioned SystemDocument + /// + EmbeddingGeneratorOwnershipDocument, + + /// + /// Set the SystemDocumentType as EmbeddingGeneratorLeaseStoreInitDocument + /// Partitioned SystemDocument + /// + EmbeddingGeneratorLeaseStoreInitDocument, } } diff --git a/Microsoft.Azure.Cosmos/src/direct/SystemUsageMonitor.cs b/Microsoft.Azure.Cosmos/src/direct/SystemUsageMonitor.cs index 371b32dceb..baf688a56f 100644 --- a/Microsoft.Azure.Cosmos/src/direct/SystemUsageMonitor.cs +++ b/Microsoft.Azure.Cosmos/src/direct/SystemUsageMonitor.cs @@ -23,9 +23,7 @@ internal sealed class SystemUsageMonitor : IDisposable private readonly IDictionary recorders = new Dictionary(); private readonly Stopwatch watch = new Stopwatch(); -#pragma warning disable IDE0044 // Add readonly modifier private int pollDelayInMilliSeconds; -#pragma warning restore IDE0044 // Add readonly modifier private CancellationTokenSource cancellation; private Task periodicTask { set; get; } diff --git a/Microsoft.Azure.Cosmos/src/direct/TimerPool.cs b/Microsoft.Azure.Cosmos/src/direct/TimerPool.cs index ac9cffb42d..f95d46b822 100644 --- a/Microsoft.Azure.Cosmos/src/direct/TimerPool.cs +++ b/Microsoft.Azure.Cosmos/src/direct/TimerPool.cs @@ -106,8 +106,6 @@ private void OnTimer(Object stateInfo) return; } } -#pragma warning disable CDX1002 -#pragma warning disable CDX1003 try { // get the current tick count which will be used to compare to @@ -168,10 +166,8 @@ private void OnTimer(Object stateInfo) } catch(Exception ex) { - DefaultTrace.TraceCritical("Hit exception ex: {0}\n, stack: {1}", ex.Message, ex.StackTrace); + DefaultTrace.TraceCritical("Hit exception ex: {0}", ex.Message); } -#pragma warning restore CDX1003 -#pragma warning restore CDX1002 finally { lock(timerConcurrencyLock) @@ -199,17 +195,13 @@ public PooledTimer GetPooledTimer(int timeoutInSeconds) this.ThrowIfDisposed(); return new PooledTimer(timeoutInSeconds, this); } - -#pragma warning disable CS1572 // XML comment has a param tag, but there is no parameter by that name -/// + + /// /// get a timer with timeout specified as a TimeSpan /// /// /// -#pragma warning disable CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do) public PooledTimer GetPooledTimer(TimeSpan timeout) -#pragma warning restore CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do) -#pragma warning restore CS1572 // XML comment has a param tag, but there is no parameter by that name { this.ThrowIfDisposed(); return new PooledTimer(timeout, this); diff --git a/Microsoft.Azure.Cosmos/src/direct/TransportClient.cs b/Microsoft.Azure.Cosmos/src/direct/TransportClient.cs index 5c8ac5059b..f66942e5b2 100644 --- a/Microsoft.Azure.Cosmos/src/direct/TransportClient.cs +++ b/Microsoft.Azure.Cosmos/src/direct/TransportClient.cs @@ -1199,6 +1199,7 @@ protected static string GetErrorResponse(StoreResponse storeResponse, string def protected static string GetErrorFromStream(Stream responseStream) { + //$ISSUE-Review-mayapainter-2025/06/16 We cannot always assume that responseStream will be text. using (responseStream) { return new StreamReader(responseStream).ReadToEnd(); diff --git a/Microsoft.Azure.Cosmos/src/direct/TransportSerialization.cs b/Microsoft.Azure.Cosmos/src/direct/TransportSerialization.cs index df2d400212..cb3f2328f3 100644 --- a/Microsoft.Azure.Cosmos/src/direct/TransportSerialization.cs +++ b/Microsoft.Azure.Cosmos/src/direct/TransportSerialization.cs @@ -109,6 +109,7 @@ internal static SerializedRequest BuildRequestForProxy( Guid activityId, BufferProvider bufferProvider, string globalDatabaseAccountName, + string regionalDatabaseAccountName, out int headerSize, out int? bodySize) { @@ -124,6 +125,10 @@ internal static SerializedRequest BuildRequestForProxy( rntbdRequest.globalDatabaseAccountName.value.valueBytes = BytesSerializer.GetBytesForString(globalDatabaseAccountName, rntbdRequest); rntbdRequest.globalDatabaseAccountName.isPresent = true; + // Set RegionalDatabaseAccountName + rntbdRequest.regionalDatabaseAccountName.value.valueBytes = BytesSerializer.GetBytesForString(regionalDatabaseAccountName, rntbdRequest); + rntbdRequest.regionalDatabaseAccountName.isPresent = true; + return BuildRequestCore( request, ref rntbdRequest, @@ -205,6 +210,7 @@ private static SerializedRequest BuildRequestCore( TransportSerialization.AddEmitVerboseTracesInQuery(requestHeaders, rntbdRequest); TransportSerialization.AddCanCharge(requestHeaders, rntbdRequest); TransportSerialization.AddCanThrottle(requestHeaders, rntbdRequest); + TransportSerialization.AddIsRequestIgnoredForAutoscaleReporting(requestHeaders, rntbdRequest); TransportSerialization.AddProfileRequest(requestHeaders, rntbdRequest); TransportSerialization.AddEnableLowPrecisionOrderBy(requestHeaders, rntbdRequest); TransportSerialization.AddPageSize(requestHeaders, rntbdRequest); @@ -267,6 +273,7 @@ private static SerializedRequest BuildRequestCore( TransportSerialization.AddUpdateOfferStateToRestorePending(requestHeaders, rntbdRequest); TransportSerialization.AddMasterResourcesDeletionPending(requestHeaders, rntbdRequest); TransportSerialization.AddIsInternalServerlessRequest(requestHeaders, rntbdRequest); + TransportSerialization.AddAllowInternalServerlessOfferRead(requestHeaders, rntbdRequest); TransportSerialization.AddOfferReplaceRURedistribution(requestHeaders, rntbdRequest); TransportSerialization.AddIsMaterializedViewSourceSchemaReplaceBatchRequest(requestHeaders, rntbdRequest); TransportSerialization.AddIsCassandraAlterTypeRequest(request, rntbdRequest); @@ -370,12 +377,28 @@ private static SerializedRequest BuildRequestCore( TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.PopulateBinaryEncodingMigratorProgress, requestHeaders.PopulateBinaryEncodingMigratorProgress, rntbdRequest.populateBinaryEncodingMigratorProgress, rntbdRequest); TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.AllowUpdatingIsPhysicalMigrationInProgress, requestHeaders.AllowUpdatingIsPhysicalMigrationInProgress, rntbdRequest.allowUpdatingIsPhysicalMigrationInProgress, rntbdRequest); TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.IncludeColdTier, requestHeaders.IncludeColdTier, rntbdRequest.includeColdTier, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.CreatePKRangesWithStatusOffline, requestHeaders.CreatePKRangesWithStatusOffline, rntbdRequest.createPKRangesWithStatusOffline, rntbdRequest); TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.PopulateVectorIndexProgress, requestHeaders.PopulateVectorIndexProgress, rntbdRequest.populateVectorIndexProgress, rntbdRequest); TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.PopulateThroughputPoolInfo, requestHeaders.PopulateThroughputPoolInfo, rntbdRequest.populateThroughputPoolInfo, rntbdRequest); TransportSerialization.FillTokenFromHeader(request, WFConstants.BackendHeaders.RetrieveUserStrings, requestHeaders.RetrieveUserStrings, rntbdRequest.retrieveUserStrings, rntbdRequest); TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.PopulateVectorIndexAggregateProgress, requestHeaders.PopulateVectorIndexAggregateProgress, rntbdRequest.populateVectorIndexAggregateProgress, rntbdRequest); TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.AllowTopologyUpsertWithoutIntent, requestHeaders.AllowTopologyUpsertWithoutIntent, rntbdRequest.allowTopologyUpsertWithoutIntent, rntbdRequest); TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.ReadGlobalCommittedData, requestHeaders.ReadGlobalCommittedData, rntbdRequest.readGlobalCommittedData, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.IsSoftDeletionOrRecoveryOperation, requestHeaders.IsSoftDeletionOrRecoveryOperation, rntbdRequest.isSoftDeletionOrRecoveryOperation, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.WorkloadId, requestHeaders.WorkloadId, rntbdRequest.workloadId, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.OriginalAuthorizationTokenType, requestHeaders.OriginalAuthTokenType, rntbdRequest.originalAuthTokenType, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, WFConstants.BackendHeaders.AllowBarrierRequestWhenRWStatusRevoked, requestHeaders.AllowBarrierRequestWhenRWStatusRevoked, rntbdRequest.allowBarrierRequestWhenRWStatusRevoked, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.IsEmbeddingGeneratorRequest, requestHeaders.IsEmbeddingGeneratorRequest, rntbdRequest.isEmbeddingGeneratorRequest, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.BypassSoftDeletionBlocking, requestHeaders.BypassSoftDeletionBlocking, rntbdRequest.bypassSoftDeletionBlocking, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.IsStrongConsistencyStoreClient, requestHeaders.IsStrongConsistencyStoreClient, rntbdRequest.isStrongConsistencyStoreClient, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.ShouldProcessOnlyInHubRegion, requestHeaders.ShouldProcessOnlyInHubRegion, rntbdRequest.shouldProcessOnlyInHubRegion, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, WFConstants.BackendHeaders.PopulateGlobalEpochRecordCount, requestHeaders.PopulateGlobalEpochRecordCount, rntbdRequest.populateGlobalEpochRecordCount, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, WFConstants.BackendHeaders.PopulateCanFailoverManagerAccessDocumentStore, requestHeaders.PopulateCanFailoverManagerAccessDocumentStore, rntbdRequest.populateCanFailoverManagerAccessDocumentStore, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, WFConstants.BackendHeaders.PopulateGloballyAcceptedFailoverPolicy, requestHeaders.PopulateGloballyAcceptedFailoverPolicy, rntbdRequest.populateGloballyAcceptedFailoverPolicy, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, WFConstants.BackendHeaders.PopulateGlobalStateWriteQuorumRegionsSet, requestHeaders.PopulateGlobalStateWriteQuorumRegionsSet, rntbdRequest.populateGlobalStateWriteQuorumRegionsSet, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.DistributedTransactionId, requestHeaders.DistributedTransactionId, rntbdRequest.distributedTransactionId, rntbdRequest); + TransportSerialization.FillTokenFromHeader(request, HttpConstants.HttpHeaders.HybridLogicalClockTimestamp, requestHeaders.HybridLogicalClockTimestamp, rntbdRequest.hybridLogicalClockTimestamp, rntbdRequest); + // will be null in case of direct, which is fine - BE will use the value from the connection context message. // When this is used in Gateway, the header value will be populated with the proxied HTTP request's header, and // BE will respect the per-request value. @@ -479,7 +502,12 @@ private static SerializedRequest BuildRequestCore( return new SerializedRequest(contextMessage, clonedStream); } - internal static byte[] BuildContextRequest(Guid activityId, UserAgentContainer userAgent, RntbdConstants.CallerId callerId, bool enableChannelMultiplexing) + internal static byte[] BuildContextRequest( + Guid activityId, + UserAgentContainer userAgent, + RntbdConstants.CallerId callerId, + bool enableChannelMultiplexing, + RntbdConstants.RntbdMutualTlsAuthMode? mutualTlsAuthMode = default) { byte[] activityIdBytes = activityId.ToByteArray(); @@ -503,6 +531,12 @@ internal static byte[] BuildContextRequest(Guid activityId, UserAgentContainer u request.enableChannelMultiplexing.isPresent = true; request.enableChannelMultiplexing.value.valueByte = enableChannelMultiplexing ? (byte)1 : (byte)0; + if(mutualTlsAuthMode is not null) + { + request.mutualTlsAuthMode.isPresent = true; + request.mutualTlsAuthMode.value.valueByte = (byte)mutualTlsAuthMode.Value; + } + int length = (sizeof(UInt32) + sizeof(UInt16) + sizeof(UInt16) + activityIdBytes.Length); // header length += request.CalculateLength(); // tokens @@ -580,6 +614,10 @@ internal static RntbdConstants.RntbdOperationType GetRntbdOperationType(Operatio return RntbdConstants.RntbdOperationType.CompleteUserTransaction; case OperationType.MetadataCheckAccess: return RntbdConstants.RntbdOperationType.MetadataCheckAccess; + case OperationType.CommitDistributedTransaction: + return RntbdConstants.RntbdOperationType.CommitDistributedTransaction; + case OperationType.AbortDistributedTransaction: + return RntbdConstants.RntbdOperationType.AbortDistributedTransaction; #if !COSMOSCLIENT case OperationType.Crash: return RntbdConstants.RntbdOperationType.Crash; @@ -653,9 +691,27 @@ internal static RntbdConstants.RntbdOperationType GetRntbdOperationType(Operatio return RntbdConstants.RntbdOperationType.Truncate; case OperationType.RelocateLeakedTentativeWrites: return RntbdConstants.RntbdOperationType.RelocateLeakedTentativeWrites; + case OperationType.ExternalPreBackup: + return RntbdConstants.RntbdOperationType.ExternalPreBackup; + case OperationType.ExternalBackup: + return RntbdConstants.RntbdOperationType.ExternalBackup; + case OperationType.CheckExternalBackupStatus: + return RntbdConstants.RntbdOperationType.CheckExternalBackupStatus; + case OperationType.ExternalBackupRestore: + return RntbdConstants.RntbdOperationType.ExternalBackupRestore; + case OperationType.CheckExternalBackupRestoreStatus: + return RntbdConstants.RntbdOperationType.CheckExternalBackupRestoreStatus; + case OperationType.PrepareDistributedTransaction: + return RntbdConstants.RntbdOperationType.PrepareDistributedTransaction; + case OperationType.CancelExternalBackup: + return RntbdConstants.RntbdOperationType.CancelExternalBackup; + case OperationType.CancelExternalBackupRestore: + return RntbdConstants.RntbdOperationType.CancelExternalBackupRestore; #endif - case OperationType.AddComputeGatewayRequestCharges: + case OperationType.AddComputeGatewayRequestCharges: return RntbdConstants.RntbdOperationType.AddComputeGatewayRequestCharges; + case OperationType.QueryPlan: + return RntbdConstants.RntbdOperationType.QueryPlan; default: throw new ArgumentException( string.Format(CultureInfo.InvariantCulture, "Invalid operation type: {0}", operationType), @@ -725,6 +781,10 @@ internal static RntbdConstants.RntbdResourceType GetRntbdResourceType(ResourceTy return RntbdConstants.RntbdResourceType.RetriableWriteCachedResponse; case ResourceType.EncryptionScope: return RntbdConstants.RntbdResourceType.EncryptionScope; + case ResourceType.AzureRbac: + return RntbdConstants.RntbdResourceType.AzureRbac; + case ResourceType.DistributedTransactionBatch: + return RntbdConstants.RntbdResourceType.DistributedTransactionBatch; #if !COSMOSCLIENT case ResourceType.Module: return RntbdConstants.RntbdResourceType.Module; @@ -887,6 +947,10 @@ private static void SetResourceIdHeadersFromUri(DocumentServiceRequest request, rntbdRequest.encryptionScopeName.value.valueBytes = BytesSerializer.GetBytesForString(fragments[1], rntbdRequest); rntbdRequest.encryptionScopeName.isPresent = true; break; + case Paths.AzureRbacPathSegment: + rntbdRequest.azureRbacName.value.valueBytes = BytesSerializer.GetBytesForString(fragments[1], rntbdRequest); + rntbdRequest.azureRbacName.isPresent = true; + break; default: throw new BadRequestException(); } @@ -1290,7 +1354,19 @@ private static void AddCanCharge(RequestNameValueCollection requestHeaders, Rntb rntbdRequest.canCharge.isPresent = true; } } - + + private static void AddIsRequestIgnoredForAutoscaleReporting(RequestNameValueCollection requestHeaders, RntbdConstants.Request rntbdRequest) + { + if (!string.IsNullOrEmpty(requestHeaders.IsRequestIgnoredForAutoscaleReporting)) + { + rntbdRequest.isRequestIgnoredForAutoscaleReporting.value.valueByte = (requestHeaders.IsRequestIgnoredForAutoscaleReporting. + Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase)) + ? (byte)0x01 + : (byte)0x00; + rntbdRequest.isRequestIgnoredForAutoscaleReporting.isPresent = true; + } + } + private static void AddCanThrottle(RequestNameValueCollection requestHeaders, RntbdConstants.Request rntbdRequest) { if (!string.IsNullOrEmpty(requestHeaders.CanThrottle)) @@ -1742,6 +1818,18 @@ private static void AddIsInternalServerlessRequest(RequestNameValueCollection re } } + private static void AddAllowInternalServerlessOfferRead(RequestNameValueCollection requestHeaders, RntbdConstants.Request rntbdRequest) + { + if (!string.IsNullOrEmpty(requestHeaders.AllowInternalServerlessOfferRead)) + { + rntbdRequest.allowInternalServerlessOfferRead.value.valueByte = (requestHeaders.AllowInternalServerlessOfferRead. + Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase)) + ? (byte)0x01 + : (byte)0x00; + rntbdRequest.allowInternalServerlessOfferRead.isPresent = true; + } + } + private static void AddCorrelatedActivityId(RequestNameValueCollection requestHeaders, RntbdConstants.Request rntbdRequest) { string headerValue = requestHeaders.CorrelatedActivityId; @@ -2222,6 +2310,27 @@ private static void FillTokenFromHeader( token.value.valueULong = valueULong; break; + case RntbdTokenTypes.UShort: + ushort valueUShort; + if (headerStringValue != null) + { + if (!ushort.TryParse(headerStringValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out valueUShort)) + { + throw new BadRequestException(String.Format(CultureInfo.CurrentUICulture, RMResources.InvalidHeaderValue, headerStringValue, headerName)); + } + } + else + { + if (!(headerValue is ushort ushortValue)) + { + throw new BadRequestException(String.Format(CultureInfo.CurrentUICulture, RMResources.InvalidHeaderValue, headerValue, headerName)); + } + + valueUShort = ushortValue; + } + + token.value.valueUShort = valueUShort; + break; case RntbdTokenTypes.Long: int valueLong; if (headerStringValue != null) @@ -2452,6 +2561,15 @@ private static void AddSystemDocumentTypeHeader(RequestNameValueCollection reque case SystemDocumentType.MaterializedViewLeaseStoreInitDocument: rntbdSystemDocumentType = RntbdConstants.RntbdSystemDocumentType.MaterializedViewLeaseStoreInitDocument; break; + case SystemDocumentType.EmbeddingGeneratorLeaseDocument: + rntbdSystemDocumentType = RntbdConstants.RntbdSystemDocumentType.EmbeddingGeneratorLeaseDocument; + break; + case SystemDocumentType.EmbeddingGeneratorOwnershipDocument: + rntbdSystemDocumentType = RntbdConstants.RntbdSystemDocumentType.EmbeddingGeneratorOwnershipDocument; + break; + case SystemDocumentType.EmbeddingGeneratorLeaseStoreInitDocument: + rntbdSystemDocumentType = RntbdConstants.RntbdSystemDocumentType.EmbeddingGeneratorLeaseStoreInitDocument; + break; default: throw new BadRequestException(String.Format(CultureInfo.CurrentUICulture, RMResources.InvalidEnumValue, requestHeaders.SystemDocumentType, typeof(SystemDocumentType).Name)); diff --git a/Microsoft.Azure.Cosmos/src/direct/UnixDateTimeConverter.cs b/Microsoft.Azure.Cosmos/src/direct/UnixDateTimeConverter.cs index 0a8f455efb..be6bdc96a2 100644 --- a/Microsoft.Azure.Cosmos/src/direct/UnixDateTimeConverter.cs +++ b/Microsoft.Azure.Cosmos/src/direct/UnixDateTimeConverter.cs @@ -21,9 +21,7 @@ namespace Microsoft.Azure.Documents #endif sealed class UnixDateTimeConverter : DateTimeConverterBase { -#pragma warning disable IDE0044 // Add readonly modifier private static DateTime UnixStartTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); -#pragma warning restore IDE0044 // Add readonly modifier /// /// Writes the JSON representation of the DateTime object. diff --git a/Microsoft.Azure.Cosmos/src/direct/ValueStopwatch.cs b/Microsoft.Azure.Cosmos/src/direct/ValueStopwatch.cs index fbaf348669..09f41eb6d0 100644 --- a/Microsoft.Azure.Cosmos/src/direct/ValueStopwatch.cs +++ b/Microsoft.Azure.Cosmos/src/direct/ValueStopwatch.cs @@ -26,18 +26,17 @@ internal struct ValueStopwatch public static readonly long Frequency = Stopwatch.Frequency; /// public static readonly bool IsHighResolution = Stopwatch.IsHighResolution; - -#pragma warning disable CS1570 // XML comment has badly formed XML -/// + + /// /// We pack everything into a single long, so using this doesn't inflate any objects with it as a field. /// /// State is interpreted as follows /// - state == 0 /// * IsRunning == false /// * We have never started - /// - state > 0 + /// - state > 0 /// * IsRunning == true - /// - state < 0 + /// - state < 0 /// * IsRunning == false /// * We have been started and stopped /// * ElapsedTicks == Math.Abs(state) @@ -46,7 +45,6 @@ internal struct ValueStopwatch /// to account for any existing duration. /// private long state; -#pragma warning restore CS1570 // XML comment has badly formed XML /// public readonly bool IsRunning diff --git a/Microsoft.Azure.Cosmos/src/direct/VectorIndexPath.cs b/Microsoft.Azure.Cosmos/src/direct/VectorIndexPath.cs index efbc8bab30..99479833cb 100644 --- a/Microsoft.Azure.Cosmos/src/direct/VectorIndexPath.cs +++ b/Microsoft.Azure.Cosmos/src/direct/VectorIndexPath.cs @@ -39,12 +39,14 @@ namespace Microsoft.Azure.Documents /// { /// "path": "/vector3", /// "type": "quantizedFlat", + /// "quantizerType": "product", /// "quantizationByteSize": 20, /// "vectorIndexShardKey": ["/Country"] /// }, /// { /// "path": "/vector3", /// "type": "diskann", + /// "quantizerType": "product", /// "quantizationByteSize": 32, /// "indexingSearchListSize": 100, /// "vectorIndexShardKey": ["/Country", "ZipCode"] @@ -112,6 +114,24 @@ public int? QuantizationByteSize } } + /// + /// Gets or sets the quantizer type for the vector index path. + /// + /// + /// + [JsonProperty(PropertyName = Constants.Properties.QuantizerType, NullValueHandling = NullValueHandling.Ignore)] + public QuantizerType? QuantType + { + get + { + return base.GetValue(Constants.Properties.QuantizerType); + } + set + { + base.SetValue(Constants.Properties.QuantizerType, value); + } + } + /// /// Gets or sets the indexing search list size for the vector index path. /// @@ -154,6 +174,7 @@ public object Clone() clonedVectorIndexPath.Path = this.Path; clonedVectorIndexPath.Type = this.Type; clonedVectorIndexPath.QuantizationByteSize = this.QuantizationByteSize; + clonedVectorIndexPath.QuantType = this.QuantType; clonedVectorIndexPath.IndexingSearchListSize = this.IndexingSearchListSize; clonedVectorIndexPath.VectorIndexShardKey = this.VectorIndexShardKey; return clonedVectorIndexPath; @@ -164,6 +185,7 @@ internal override void OnSave() base.SetValue(Constants.Properties.Path, this.Path); base.SetValue(Constants.Properties.Type, this.Type); base.SetValue(Constants.Properties.QuantizationByteSize, this.QuantizationByteSize); + base.SetValue(Constants.Properties.QuantizerType, this.QuantType); base.SetValue(Constants.Properties.IndexingSearchListSize, this.IndexingSearchListSize); base.SetValue(Constants.Properties.VectorIndexShardKey, this.VectorIndexShardKey); } diff --git a/Microsoft.Azure.Cosmos/src/direct/VectorSessionToken.cs b/Microsoft.Azure.Cosmos/src/direct/VectorSessionToken.cs index 96f76360c1..c8ec32cbae 100644 --- a/Microsoft.Azure.Cosmos/src/direct/VectorSessionToken.cs +++ b/Microsoft.Azure.Cosmos/src/direct/VectorSessionToken.cs @@ -26,9 +26,7 @@ namespace Microsoft.Azure.Documents internal sealed class VectorSessionToken : ISessionToken { private static readonly IReadOnlyDictionary DefaultLocalLsnByRegion = new Dictionary(0); -#pragma warning disable SA1203 private const string SessionTokenFalseProgressMergeDisabled = "AZURE_COSMOS_SESSION_TOKEN_FALSE_PROGRESS_MERGE_DISABLED"; -#pragma warning restore SA1203 private const char SegmentSeparator = '#'; private const string SegmentSeparatorString = "#"; private const char RegionProgressSeparator = '='; @@ -38,9 +36,7 @@ internal sealed class VectorSessionToken : ISessionToken private readonly long globalLsn; #pragma warning restore IDE0032 // Use auto property private readonly IReadOnlyDictionary localLsnByRegion; -#pragma warning disable IDE0044 // Add readonly modifier private static bool isFalseProgressMergeDisabled = string.Equals(Environment.GetEnvironmentVariable(SessionTokenFalseProgressMergeDisabled), "true", -#pragma warning restore IDE0044 // Add readonly modifier StringComparison.OrdinalIgnoreCase); private VectorSessionToken(long version, long globalLsn, IReadOnlyDictionary localLsnByRegion, string sessionToken = null) @@ -145,6 +141,7 @@ public bool IsValid(ISessionToken otherSessionToken) } } + if (other.version == this.version && other.localLsnByRegion.Count != this.localLsnByRegion.Count) { throw new InternalServerErrorException( diff --git a/Microsoft.Azure.Cosmos/src/direct/WFConstants.cs b/Microsoft.Azure.Cosmos/src/direct/WFConstants.cs index dab73fba52..6fedcdf525 100644 Binary files a/Microsoft.Azure.Cosmos/src/direct/WFConstants.cs and b/Microsoft.Azure.Cosmos/src/direct/WFConstants.cs differ diff --git a/Microsoft.Azure.Cosmos/src/direct/WindowsSystemUtilizationReader.cs b/Microsoft.Azure.Cosmos/src/direct/WindowsSystemUtilizationReader.cs index c59fa0ea9c..69f35e498f 100644 --- a/Microsoft.Azure.Cosmos/src/direct/WindowsSystemUtilizationReader.cs +++ b/Microsoft.Azure.Cosmos/src/direct/WindowsSystemUtilizationReader.cs @@ -81,11 +81,8 @@ private static class NativeMethods /// [DllImport("kernel32.dll", SetLastError = true)] internal static extern bool GetSystemTimes(out long idle, out long kernel, out long user); - -#pragma warning disable CS1572 // XML comment has a param tag, but there is no parameter by that name -#pragma warning disable CS1572 // XML comment has a param tag, but there is no parameter by that name -#pragma warning disable CS1572 // XML comment has a param tag, but there is no parameter by that name -/// + + /// /// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-globalmemorystatusex /// /// @@ -93,12 +90,7 @@ private static class NativeMethods /// /// [DllImport("kernel32.dll", SetLastError = true)] -#pragma warning restore CS1572 // XML comment has a param tag, but there is no parameter by that name -#pragma warning restore CS1572 // XML comment has a param tag, but there is no parameter by that name -#pragma warning restore CS1572 // XML comment has a param tag, but there is no parameter by that name -#pragma warning disable CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do) internal static extern bool GlobalMemoryStatusEx(out MemoryInfo memInfo); -#pragma warning restore CS1573 // Parameter has no matching param tag in the XML comment (but other parameters do) [StructLayout(LayoutKind.Sequential)] internal struct MemoryInfo diff --git a/Microsoft.Azure.Cosmos/src/direct/msdata_sync.ps1 b/Microsoft.Azure.Cosmos/src/direct/msdata_sync.ps1 index dc54c2883a..228093614c 100644 --- a/Microsoft.Azure.Cosmos/src/direct/msdata_sync.ps1 +++ b/Microsoft.Azure.Cosmos/src/direct/msdata_sync.ps1 @@ -1,4 +1,4 @@ -$baseDir = "Q:\src\CosmosDB" +$baseDir = "Q:\CosmosDB" $sourceDir = @( "\Product\SDK\.net\Microsoft.Azure.Cosmos.Direct\src\", @@ -6,8 +6,10 @@ $sourceDir = "\Product\Microsoft.Azure.Documents\SharedFiles\Routing\", "\Product\Microsoft.Azure.Documents\SharedFiles\Rntbd2\", "\Product\Microsoft.Azure.Documents\SharedFiles\Rntbd\", + "\Product\Microsoft.Azure.Documents\SharedFiles\Rntbd\rntbdtokens\", "\Product\SDK\.net\Microsoft.Azure.Documents.Client\LegacyXPlatform\", "\Product\Cosmos\Core\Core.Trace\", + "\Product\Cosmos\Core\Core\Utilities\", "\Product\Microsoft.Azure.Documents\SharedFiles\", "\Product\Microsoft.Azure.Documents\SharedFiles\Collections\", "\Product\Microsoft.Azure.Documents\SharedFiles\Query\", @@ -16,10 +18,10 @@ $sourceDir = $syncCopyFile="msdata_sync.ps1" $exclueList = @( + "AssemblyKeys.cs", "BaseTransportClient.cs", "CpuReaderBase.cs", "LinuxCpuReader.cs", - "MaterializedViews.cs", "MemoryLoad.cs", "MemoryLoadHistory.cs", "UnsupportedCpuReader.cs", @@ -75,8 +77,8 @@ foreach ($file in Get-ChildItem . -Name ) if (! ($fileFound -or $exclueList -contains $file)) { - Write-Error "$file $fileFound" - break; + Write-Warning "File not found in any source directory: $file" + continue; } if ($fileFound) diff --git a/Microsoft.Azure.Cosmos/src/direct/rntbd2/TransportClient.cs b/Microsoft.Azure.Cosmos/src/direct/rntbd2/TransportClient.cs index e75e5cf036..23d6d9cf28 100644 --- a/Microsoft.Azure.Cosmos/src/direct/rntbd2/TransportClient.cs +++ b/Microsoft.Azure.Cosmos/src/direct/rntbd2/TransportClient.cs @@ -10,6 +10,7 @@ namespace Microsoft.Azure.Documents.Rntbd using System.Text; using System.Threading; using System.Threading.Tasks; + using System.Security.Cryptography.X509Certificates; using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Documents.FaultInjection; using Microsoft.Azure.Documents.Telemetry; @@ -97,6 +98,8 @@ public TransportClient(Options clientOptions, IChaosInterceptor chaosInterceptor clientOptions.EnableChannelMultiplexing, clientOptions.MemoryStreamPool, clientOptions.RemoteCertificateValidationCallback, + clientOptions.ClientCertificateFunction, + clientOptions.ClientCertificateFailureHandler, clientOptions.DnsResolutionFunction), chaosInterceptor); } @@ -354,6 +357,17 @@ internal override Task OpenConnectionAsync( public event Action OnDisableRntbdChannel; + public Func IsDisableRntbdChannelActionRegistered + { + get + { + return () => + { + return (this.OnDisableRntbdChannel != null); + }; + } + } + // Examines storeResponse and raises an event if this is the first time // this transport client sees the "disable RNTBD channel" header set to // true by the back-end. @@ -513,6 +527,10 @@ public TimeSpan TimerPoolResolution public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; internal set; } + public Func ClientCertificateFunction { get; internal set; } + + public Action ClientCertificateFailureHandler { get; internal set; } + /// /// Override for DNS resolution callbacks for RNTBD connections. /// diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BarrierRequestReplicaRetryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BarrierRequestReplicaRetryTests.cs new file mode 100644 index 0000000000..1b06161dab --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BarrierRequestReplicaRetryTests.cs @@ -0,0 +1,520 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Net; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Collections; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using static Microsoft.Azure.Cosmos.SDK.EmulatorTests.TransportClientHelper; + + [TestClass] + [TestCategory("MultiRegion")] + public class BarrierRequestReplicaRetryTests + { + private string connectionString; + private CosmosClient cosmosClient; + private Cosmos.Database database; + private Container container; + + [TestInitialize] + public async Task TestInitialize() + { + this.connectionString = ConfigurationManager.GetEnvironmentVariable("COSMOSDB_MULTI_REGION", string.Empty); + if (string.IsNullOrEmpty(this.connectionString)) + { + Assert.Fail("Set environment variable COSMOSDB_MULTI_REGION to run the tests"); + } + + this.cosmosClient = new CosmosClient( + this.connectionString, + new CosmosClientOptions + { + ConnectionMode = ConnectionMode.Direct, + ConsistencyLevel = Cosmos.ConsistencyLevel.Strong + }); + + string uniqueDbName = "BarrierTestDb_" + Guid.NewGuid().ToString(); + this.database = await this.cosmosClient.CreateDatabaseIfNotExistsAsync(uniqueDbName); + + string uniqueContainerName = "BarrierTestContainer_" + Guid.NewGuid().ToString(); + this.container = await this.database.CreateContainerIfNotExistsAsync( + new ContainerProperties(uniqueContainerName, "/pk") + { + PartitionKey = new PartitionKeyDefinition { Paths = new System.Collections.ObjectModel.Collection { "/pk" } } + }); + } + + [TestCleanup] + public async Task Cleanup() + { + if (this.database != null) + { + try + { + await this.database.DeleteAsync(); + } + catch { } + } + + this.cosmosClient?.Dispose(); + } + + [TestMethod] + [Owner("aavasthy")] + [Description("Validates that write barrier requests retry on primary when secondary replica returns 410/1022")] + [TestCategory("MultiRegion")] + public async Task WriteBarrier_SecondaryReplicaLeaseNotFound_RetriesOnPrimary() + { + // Shared state protected by lock + object stateLock = new object(); + int barrierRequestCount = 0; + int forceRefreshAddressCacheSeenCount = 0; + long targetLsn = 0; + + CosmosClientOptions clientOptions = new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Direct, + ConsistencyLevel = Cosmos.ConsistencyLevel.Strong, + TransportClientHandlerFactory = (transport) => new TransportClientWrapper( + transport, + interceptorAfterResult: (request, storeResponse) => + { + lock (stateLock) + { + // Force barrier request by setting GlobalCommittedLSN behind LSN on write + if (storeResponse.StatusCode == HttpStatusCode.Created) + { + if (targetLsn == 0) + { + targetLsn = storeResponse.LSN; + // This triggers barrier request in ConsistencyWriter.ApplySessionToken + storeResponse.Headers.Set(WFConstants.BackendHeaders.NumberOfReadRegions, "2"); + storeResponse.Headers.Set(WFConstants.BackendHeaders.GlobalCommittedLSN, "0"); // Behind LSN + storeResponse.Headers.Set(WFConstants.BackendHeaders.LSN, storeResponse.LSN.ToString(CultureInfo.InvariantCulture)); + } + } + + // Handle barrier (HEAD) requests + if (request.OperationType == OperationType.Head) + { + barrierRequestCount++; + + if (request.RequestContext != null && request.RequestContext.ForceRefreshAddressCache) + { + forceRefreshAddressCacheSeenCount++; + } + + // First barrier request returns 410/1022 + if (barrierRequestCount == 1) + { + DictionaryNameValueCollection headers = new DictionaryNameValueCollection(); + headers.Set(WFConstants.BackendHeaders.SubStatus, + ((int)SubStatusCodes.LeaseNotFound).ToString(CultureInfo.InvariantCulture)); + + return new StoreResponse() + { + Status = 410, + Headers = headers, + ResponseBody = new MemoryStream(Encoding.UTF8.GetBytes("Lease not found")) + }; + } + // Second barrier request (to primary with ForceRefreshAddressCache) succeeds + else if (barrierRequestCount == 2) + { + DictionaryNameValueCollection successHeaders = new DictionaryNameValueCollection(); + successHeaders.Set(WFConstants.BackendHeaders.NumberOfReadRegions, "2"); + successHeaders.Set(WFConstants.BackendHeaders.GlobalCommittedLSN, + targetLsn.ToString(CultureInfo.InvariantCulture)); + successHeaders.Set(WFConstants.BackendHeaders.LSN, + targetLsn.ToString(CultureInfo.InvariantCulture)); + + return new StoreResponse() + { + Status = 200, + Headers = successHeaders, + ResponseBody = Stream.Null + }; + } + } + } + + return storeResponse; + }) + }; + + using CosmosClient client = new CosmosClient(this.connectionString, clientOptions); + Container testContainer = client.GetContainer(this.database.Id, this.container.Id); + + // Act + ToDoActivity item = ToDoActivity.CreateRandomToDoActivity(); + ItemResponse response = await testContainer.CreateItemAsync( + item, + new Cosmos.PartitionKey(item.pk)); + + // Assert + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode, "Write should succeed after barrier retry"); + + lock (stateLock) + { + if (barrierRequestCount > 0) + { + Assert.AreEqual(2, barrierRequestCount, "Should make exactly 2 barrier requests (secondary then primary)"); + Assert.IsTrue(forceRefreshAddressCacheSeenCount > 0, "Second barrier request should have ForceRefreshAddressCache=true"); + } + else + { + Assert.Inconclusive("Barrier requests were not triggered. This test requires a multi-region Strong consistency account."); + } + } + } + + [TestMethod] + [Owner("aavasthy")] + [Description("Validates that read barrier requests retry on primary when quorum replica returns 410/1022")] + [TestCategory("MultiRegion")] + public async Task ReadBarrier_QuorumReplicaLeaseNotFound_RetriesOnPrimary() + { + ToDoActivity item = ToDoActivity.CreateRandomToDoActivity(); + await this.container.CreateItemAsync(item, new Cosmos.PartitionKey(item.pk)); + + object stateLock = new object(); + int barrierRequestCount = 0; + int forceRefreshAddressCacheSeenCount = 0; + const long targetLsn = 100; + bool shouldInterceptRead = true; + string targetItemId = item.id; + + CosmosClientOptions clientOptions = new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Direct, + ConsistencyLevel = Cosmos.ConsistencyLevel.Strong, + TransportClientHandlerFactory = (transport) => new TransportClientWrapper( + transport, + interceptorAfterResult: (request, storeResponse) => + { + lock (stateLock) + { + // Only manipulate the read response for our specific test item when flag is set + if (request.OperationType == OperationType.Read && + storeResponse.StatusCode == HttpStatusCode.OK && + request.ResourceAddress != null && + request.ResourceAddress.Contains(targetItemId) && + shouldInterceptRead) + { + shouldInterceptRead = false; + // This triggers barrier in QuorumReader.ReadQuorumAsync + storeResponse.Headers.Set(WFConstants.BackendHeaders.NumberOfReadRegions, "2"); + storeResponse.Headers.Set(WFConstants.BackendHeaders.LSN, targetLsn.ToString(CultureInfo.InvariantCulture)); + storeResponse.Headers.Set(WFConstants.BackendHeaders.ItemLSN, targetLsn.ToString(CultureInfo.InvariantCulture)); + storeResponse.Headers.Set(WFConstants.BackendHeaders.GlobalCommittedLSN, "50"); // Behind LSN + } + + // Handle barrier (HEAD) requests + if (request.OperationType == OperationType.Head) + { + barrierRequestCount++; + + // Check if ForceRefreshAddressCache is set + if (request.RequestContext != null && request.RequestContext.ForceRefreshAddressCache) + { + forceRefreshAddressCacheSeenCount++; + } + + // First attempt (quorum replica) fails with 410/1022 + if (barrierRequestCount == 1) + { + DictionaryNameValueCollection headers = new DictionaryNameValueCollection(); + headers.Set(WFConstants.BackendHeaders.SubStatus, + ((int)SubStatusCodes.LeaseNotFound).ToString(CultureInfo.InvariantCulture)); + + return new StoreResponse() + { + Status = 410, + Headers = headers, + ResponseBody = new MemoryStream(Encoding.UTF8.GetBytes("Lease not found")) + }; + } + } + } + + return storeResponse; + }) + }; + + using CosmosClient client = new CosmosClient(this.connectionString, clientOptions); + Container testContainer = client.GetContainer(this.database.Id, this.container.Id); + + try + { + // Act + ItemResponse response = await testContainer.ReadItemAsync( + item.id, + new Cosmos.PartitionKey(item.pk)); + + // Assert + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, "Read should succeed after barrier retry"); + + lock (stateLock) + { + if (barrierRequestCount > 0) + { + Assert.IsTrue(barrierRequestCount >= 2, $"Should make at least 2 barrier requests (had {barrierRequestCount})"); + Assert.IsTrue(forceRefreshAddressCacheSeenCount > 0, "Retry should have ForceRefreshAddressCache=true"); + } + else + { + Assert.Inconclusive("Barrier requests were not triggered. This test requires a multi-region Strong consistency account."); + } + } + } + catch (CosmosException ex) + { + int finalBarrierCount; + lock (stateLock) + { + finalBarrierCount = barrierRequestCount; + } + Assert.Fail($"Unexpected exception: {ex.StatusCode} - {ex.Message}. Barrier requests: {finalBarrierCount}, ForceRefresh seen: {forceRefreshAddressCacheSeenCount > 0}"); + } + } + + [TestMethod] + [Owner("aavasthy")] + [Description("Validates that write barrier fails when primary replica returns 410/1022")] + [TestCategory("MultiRegion")] + public async Task WriteBarrier_PrimaryReplicaLeaseNotFound_Fails() + { + object stateLock = new object(); + int barrierRequestCount = 0; + int primaryBarrierCount = 0; + long targetLsn = 0; + + // Based on ConsistencyWriter code + const int expectedMaxBarrierRetries = 40; + + CosmosClientOptions clientOptions = new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Direct, + ConsistencyLevel = Cosmos.ConsistencyLevel.Strong, + RequestTimeout = TimeSpan.FromSeconds(5), // Set a shorter timeout to fail faster + TransportClientHandlerFactory = (transport) => new TransportClientWrapper( + transport, + interceptorAfterResult: (request, storeResponse) => + { + lock (stateLock) + { + // Force barrier request by setting GlobalCommittedLSN behind LSN on write + if (storeResponse.StatusCode == HttpStatusCode.Created) + { + if (targetLsn == 0) + { + targetLsn = storeResponse.LSN; + // This triggers barrier request in ConsistencyWriter.ApplySessionToken + storeResponse.Headers.Set(WFConstants.BackendHeaders.NumberOfReadRegions, "2"); + storeResponse.Headers.Set(WFConstants.BackendHeaders.GlobalCommittedLSN, "0"); // Behind LSN + storeResponse.Headers.Set(WFConstants.BackendHeaders.LSN, storeResponse.LSN.ToString(CultureInfo.InvariantCulture)); + } + } + + // Handle barrier (HEAD) requests + if (request.OperationType == OperationType.Head) + { + barrierRequestCount++; + + if (request.RequestContext != null && request.RequestContext.ForceRefreshAddressCache) + { + primaryBarrierCount++; + } + + // Always return 410/1022 for ALL barrier requests + DictionaryNameValueCollection headers = new DictionaryNameValueCollection(); + headers.Set(WFConstants.BackendHeaders.SubStatus, + ((int)SubStatusCodes.LeaseNotFound).ToString(CultureInfo.InvariantCulture)); + + return new StoreResponse() + { + Status = 410, + Headers = headers, + ResponseBody = new MemoryStream(Encoding.UTF8.GetBytes($"Lease not found - attempt {barrierRequestCount}")) + }; + } + } + + return storeResponse; + }) + }; + + using CosmosClient client = new CosmosClient(this.connectionString, clientOptions); + Container testContainer = client.GetContainer(this.database.Id, this.container.Id); + + // Act & Assert + ToDoActivity item = ToDoActivity.CreateRandomToDoActivity(); + + try + { + ItemResponse response = await testContainer.CreateItemAsync( + item, + new Cosmos.PartitionKey(item.pk)); + + int finalBarrierCount, finalPrimaryCount; + lock (stateLock) + { + finalBarrierCount = barrierRequestCount; + finalPrimaryCount = primaryBarrierCount; + } + Assert.Fail($"Write should have failed when all barrier attempts returned 410/1022. Barrier requests: {finalBarrierCount}, Primary barrier requests: {finalPrimaryCount}"); + } + catch (CosmosException ex) + { + int finalBarrierCount, finalPrimaryCount; + lock (stateLock) + { + finalBarrierCount = barrierRequestCount; + finalPrimaryCount = primaryBarrierCount; + } + + Assert.IsTrue( + ex.StatusCode == HttpStatusCode.Gone || + ex.StatusCode == HttpStatusCode.RequestTimeout || + ex.StatusCode == HttpStatusCode.ServiceUnavailable, + $"Expected Gone, RequestTimeout, or ServiceUnavailable but got {ex.StatusCode}"); + + Assert.IsTrue(finalBarrierCount > 1, $"Should have attempted multiple barrier requests (had {finalBarrierCount})"); + + // Verify at least primary replica barrier attempt was made + Assert.IsTrue(finalPrimaryCount >= 1, $"Should have attempted at least one primary barrier request (had {finalPrimaryCount})"); + + Assert.IsTrue(finalBarrierCount <= expectedMaxBarrierRetries, + $"Barrier request count {finalBarrierCount} exceeded expected max {expectedMaxBarrierRetries}"); + } + } + + [TestMethod] + [Owner("aavasthy")] + [Description("Validates that read barrier retries on primary when replica returns 410/1022, and if primary fails, SDK updates region list and retries on next available region")] + [TestCategory("MultiRegion")] + public async Task ReadBarrier_ReplicaAndPrimaryFail_RetriesOnNextAvailableRegion() + { + ToDoActivity item = ToDoActivity.CreateRandomToDoActivity(); + await this.container.CreateItemAsync(item, new Cosmos.PartitionKey(item.pk)); + + object stateLock = new object(); + int barrierRequestCount = 0; + int primaryBarrierAttempts = 0; + Dictionary uniqueRegionsContacted = new Dictionary(); + List barrierRequestRegions = new List(); + bool shouldTriggerBarrier = true; + string targetItemId = item.id; + + CosmosClientOptions clientOptions = new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Direct, + ConsistencyLevel = Cosmos.ConsistencyLevel.Strong, + TransportClientHandlerFactory = (transport) => new TransportClientWrapper( + transport, + interceptorAfterResult: (request, storeResponse) => + { + lock (stateLock) + { + // Track all regions contacted + string currentRegion = request.RequestContext?.LocationEndpointToRoute?.ToString(); + if (!string.IsNullOrEmpty(currentRegion)) + { + uniqueRegionsContacted[currentRegion] = true; + } + + if (request.OperationType == OperationType.Read && + storeResponse.StatusCode == HttpStatusCode.OK && + request.ResourceAddress?.Contains(targetItemId) == true && + shouldTriggerBarrier) + { + shouldTriggerBarrier = false; + // Force barrier check by setting GlobalCommittedLSN behind LSN + storeResponse.Headers.Set(WFConstants.BackendHeaders.NumberOfReadRegions, "2"); + storeResponse.Headers.Set(WFConstants.BackendHeaders.LSN, "100"); + storeResponse.Headers.Set(WFConstants.BackendHeaders.ItemLSN, "100"); + storeResponse.Headers.Set(WFConstants.BackendHeaders.GlobalCommittedLSN, "50"); // Behind LSN - triggers barrier + } + + if (request.OperationType == OperationType.Head) + { + barrierRequestCount++; + barrierRequestRegions.Add(currentRegion ?? "unknown"); + + bool isPrimaryRetry = request.RequestContext?.ForceRefreshAddressCache == true; + if (isPrimaryRetry) + { + primaryBarrierAttempts++; + } + + Console.WriteLine($"Barrier #{barrierRequestCount}: Region={currentRegion}, IsPrimary={isPrimaryRetry}"); + + // Simulate 410/1022 for first few attempts to force region retry + if (barrierRequestCount <= 2) + { + DictionaryNameValueCollection headers = new DictionaryNameValueCollection(); + headers.Set(WFConstants.BackendHeaders.SubStatus, + ((int)SubStatusCodes.LeaseNotFound).ToString(CultureInfo.InvariantCulture)); + + return new StoreResponse() + { + Status = 410, + Headers = headers, + ResponseBody = new MemoryStream(Encoding.UTF8.GetBytes("Lease not found")) + }; + } + } + } + + return storeResponse; + }) + }; + + using CosmosClient client = new CosmosClient(this.connectionString, clientOptions); + Container testContainer = client.GetContainer(this.database.Id, this.container.Id); + + // Act + ItemResponse response = await testContainer.ReadItemAsync( + item.id, + new Cosmos.PartitionKey(item.pk)); + + // Assert + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, "Read should eventually succeed"); + + lock (stateLock) + { + if (barrierRequestCount > 0) + { + // Verify replica failure triggers primary retry + Assert.IsTrue(barrierRequestCount >= 2, + "Should have at least 2 barrier attempts (replica + primary)"); + + Assert.IsTrue(primaryBarrierAttempts >= 1, + "Should retry on primary with ForceRefreshAddressCache=true after replica fails"); + + // 3. Verify region failover behavior + if (uniqueRegionsContacted.Count > 1) + { + Assert.IsTrue(barrierRequestCount >= 3, + "When failing over to new region, should have additional barrier attempts"); + } + } + else + { + Assert.Inconclusive("Barrier requests were not triggered. This test requires a multi-region Strong consistency account."); + } + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestArrayContainsAll.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestArrayContainsAll.xml new file mode 100644 index 0000000000..e81ebc419a --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestArrayContainsAll.xml @@ -0,0 +1,895 @@ + + + + + doc.ArrayField.ArrayContainsAll(new [] {})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAll(new [] {Convert(1, Object)})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAll(new [] {Convert(1, Object), Convert(2, Object), Convert(3, Object)})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAll(new [] {Convert(1, Object), Convert(2, Object), Convert(3, Object), Convert(2147483647, Object), Convert(-2147483648, Object)})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAll(new [] {Convert(1, Object), "hello", null, Convert(55, Object), Convert(10.123456789, Object), new [] {"world", new [] {"nested array", new AnonymousType(A = 2147483647, B = -2147483648)}}})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAll(new [] {doc.ArrayField})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + new [] {Convert(1, Object), "hello", null, Convert(55, Object), Convert(10.123456789, Object), new [] {"world", new [] {"nested array", new AnonymousType(A = 2147483647, B = -2147483648)}}}.ArrayContainsAll(new [] {doc.ArrayField})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.EnumerableField.ArrayContainsAll(new [] {Convert(5, Object), Convert(6, Object), Convert(7, Object)})).Select(doc => doc.EnumerableField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAll(new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAll(new [] {Convert(1, Object), doc.StringField, null, doc.VectorFloatField, Convert(10.123456789, Object), new [] {"world", new [] {doc.ToString(), new AnonymousType(A = doc.StringField2.StartsWith("abc"), B = -2147483648)}}})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)}.ArrayContainsAll(new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)})).Select(doc => new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)})]]> + + + + + + + + + + new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object), doc.ArrayField}.ArrayContainsAll(new [] {doc.ArrayField})).Select(doc => new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object), doc.ArrayField})]]> + + + + + + + + + + doc.StringField.ArrayContainsAll(new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)})).Select(doc => doc.StringField)]]> + + + + + + + + + + doc.StringField.ArrayContainsAll(new [] {doc.VectorFloatField})).Select(doc => doc.StringField)]]> + + + + + + + + + + (doc.ArrayField.ArrayContainsAll(new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)}) OrElse (doc.StringField.StartsWith("abc") AndAlso doc.EnumerableField.ArrayContainsAll(new [] {doc.ArrayField})))).Select(doc => new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object), doc.StringField, doc.EnumerableField})]]> + + + + + + + + + + new [] {doc.ArrayField, Convert(doc.ArrayField.ArrayContainsAll(new [] {Convert(1, Object), doc.StringField, null, doc.VectorFloatField, Convert(10.123456789, Object), new [] {"world", new [] {doc.ToString(), new AnonymousType(A = doc.StringField2.StartsWith("abc"), B = -2147483648)}}}), Object)})]]> + + + + + + + + + + doc.ArrayField).Select(item => new [] {Convert(item, Object), Convert(new [] {item}.ArrayContainsAll(new [] {item.ToString()}), Object)})]]> + + + + + + + \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestArrayContainsAny.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestArrayContainsAny.xml new file mode 100644 index 0000000000..3c6459486e --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestArrayContainsAny.xml @@ -0,0 +1,1391 @@ + + + + + doc.ArrayField.ArrayContainsAny(new [] {})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAny(new [] {Convert(1, Object)})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAny(new [] {Convert(1, Object), Convert(2, Object), Convert(3, Object)})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAny(new [] {Convert(1, Object), Convert(2, Object), Convert(3, Object), Convert(2147483647, Object), Convert(-2147483648, Object)})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAny(new [] {Convert(1, Object), "hello", null, Convert(55, Object), Convert(10.123456789, Object), new [] {"world", new [] {"nested array", new AnonymousType(A = 2147483647, B = -2147483648)}}})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAny(new [] {doc.ArrayField})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + new [] {Convert(1, Object), "hello", null, Convert(55, Object), Convert(10.123456789, Object), new [] {"world", new [] {"nested array", new AnonymousType(A = 2147483647, B = -2147483648)}}}.ArrayContainsAny(new [] {doc.ArrayField})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.EnumerableField.ArrayContainsAny(new [] {Convert(5, Object), Convert(6, Object), Convert(7, Object), Convert(100, Object)})).Select(doc => doc.EnumerableField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAny(new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + doc.ArrayField.ArrayContainsAny(new [] {Convert(1, Object), doc.StringField, null, doc.VectorFloatField, Convert(10.123456789, Object), new [] {"world", new [] {doc.ToString(), new AnonymousType(A = doc.StringField2.StartsWith("abc"), B = -2147483648)}}})).Select(doc => doc.ArrayField)]]> + + + + + + + + + + new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)}.ArrayContainsAny(new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)})).Select(doc => new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)})]]> + + + + + + + + + + new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object), doc.ArrayField}.ArrayContainsAny(new [] {doc.ArrayField})).Select(doc => new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object), doc.ArrayField})]]> + + + + + + + + + + doc.StringField.ArrayContainsAny(new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)})).Select(doc => doc.StringField)]]> + + + + + + + + + + doc.StringField.ArrayContainsAny(new [] {doc.VectorFloatField})).Select(doc => doc.StringField)]]> + + + + + + + + + + (doc.ArrayField.ArrayContainsAny(new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object)}) OrElse (doc.StringField.StartsWith("abc") AndAlso doc.EnumerableField.ArrayContainsAny(new [] {doc.ArrayField})))).Select(doc => new [] {doc.Id, Convert(doc.IntField, Object), Convert(doc.GuidField, Object), doc.StringField, doc.EnumerableField})]]> + + + + + + + + + + new [] {doc.ArrayField, Convert(doc.ArrayField.ArrayContainsAny(new [] {Convert(1, Object), doc.StringField, null, doc.VectorFloatField, Convert(10.123456789, Object), new [] {"world", new [] {doc.ToString(), new AnonymousType(A = doc.StringField2.StartsWith("abc"), B = -2147483648)}}}), Object)})]]> + + + + + + + + + + doc.ArrayField).Select(item => new [] {Convert(item, Object), Convert(new [] {item}.ArrayContainsAny(new [] {item.ToString()}), Object)})]]> + + + + + + + \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestArrayFunctions.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestArrayFunctions.xml index ecf71c89a6..9abb29b5bb 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestArrayFunctions.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestArrayFunctions.xml @@ -212,4 +212,14 @@ SELECT VALUE ARRAY_LENGTH(root["EnumerableField"]) FROM root]]> + + + + Not(doc.EnumerableField.Contains(1, EqualityComparer`1.Default)))]]> + + + + + + \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestWeightedRRF.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestWeightedRRF.xml new file mode 100644 index 0000000000..3b40b8d187 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestWeightedRRF.xml @@ -0,0 +1,77 @@ + + + + + RRF(new [] {doc.StringField.FullTextScore(new [] {"test1"}), doc.StringField.FullTextScore(new [] {"test1", "text2"})}, new [] {1, 2})).Select(doc => doc.Pk)]]> + + + + + + + + + + RRF(new [] {doc.StringField.FullTextScore(new [] {"test1"}), doc.StringField.FullTextScore(new [] {"test1", "text2"})}, new [] {1, 2})).Select(doc => doc.Pk)]]> + + + + + + + + + + RRF(new [] {doc.StringField.FullTextScore(new [] {"test1"}), doc.StringField2.FullTextScore(new [] {"test1", "test2", "test3"}), 1, 2})).Select(doc => doc.Pk)]]> + + + + + + + + + + RRF(new [] {1, 2}, new [] {doc.StringField.FullTextScore(new [] {"test1"}), doc.StringField.FullTextScore(new [] {"test1", "text2"})})).Select(doc => doc.Pk)]]> + + + + + + + + + + RRF(new [] {1, doc.StringField.FullTextScore(new [] {"test1"})}, new [] {2, doc.StringField.FullTextScore(new [] {"test1", "text2"})})).Select(doc => doc.Pk)]]> + + + + + + + + + + RRF(new [] {doc.StringField.FullTextScore(new [] {"test1"}), doc.StringField.FullTextScore(new [] {"test1"})}, new [] {2, doc.StringField.FullTextScore(new [] {"test1", "text2"})})).Select(doc => doc.Pk)]]> + + + + + + + \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/QueryAdvisorBaselineTest.QueryAdviceParse.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/QueryAdvisorBaselineTest.QueryAdviceParse.xml index 7fe8d2ac13..576fb5db26 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/QueryAdvisorBaselineTest.QueryAdviceParse.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/QueryAdvisorBaselineTest.QueryAdviceParse.xml @@ -7,7 +7,7 @@ WHERE CONTAINS(r.name, "Abc") ]]> - @@ -19,8 +19,8 @@ WHERE GetCurrentTimestamp() > 10 ]]> - diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/BuilderTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/BuilderTests.cs index 669c6bd194..916a8ee0d3 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/BuilderTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/BuilderTests.cs @@ -69,7 +69,8 @@ public async Task WhenADocumentIsCreatedWithTtlSetThenTheDocumentIsDeletedTestsA Assert.IsTrue(DateTime.TryParse(s: change.Metadata.ConflictResolutionTimestamp.ToString(), out _), message: "Invalid csrt must be a datetime value."); Assert.IsTrue(change.Metadata.Lsn > 0, message: "Invalid lsn must be a long value."); Assert.IsFalse(change.Metadata.IsTimeToLiveExpired); - + Assert.IsNull(change.Metadata.Id); + Assert.IsNull(change.Metadata.PartitionKey); // previous Assert.IsNull(change.Previous); } @@ -84,10 +85,9 @@ public async Task WhenADocumentIsCreatedWithTtlSetThenTheDocumentIsDeletedTestsA Assert.IsTrue(change.Metadata.IsTimeToLiveExpired); // previous - Assert.AreEqual(expected: "1", actual: change.Previous.id.ToString()); - Assert.AreEqual(expected: "1", actual: change.Previous.pk.ToString()); - Assert.AreEqual(expected: "Testing TTL on CFP.", actual: change.Previous.description.ToString()); - Assert.AreEqual(expected: ttlInSeconds, actual: change.Previous.ttl); + Assert.AreEqual(expected: "1", actual: change.Metadata.Id.ToString()); + Assert.AreEqual(expected: "1", actual: change.Metadata.PartitionKey.Values.FirstOrDefault()); + Assert.IsNull(change.Previous); // stop after reading delete since it is the last document in feed. stopwatch.Stop(); @@ -145,7 +145,7 @@ public async Task WhenADocumentIsCreatedWithTtlSetThenTheDocumentIsDeletedTestsA [TestMethod] [Owner("philipthomas-MSFT")] [Description("Scenario: When a document is created, then updated, and finally deleted, there should be 3 changes that will appear for that " + - "document when using ChangeFeedProcessor with AllVersionsAndDeletes set as the ChangeFeedMode.")] + "document when using ChangeFeedProcessor with AllVersionsAndDeletes set as the ChangeFeedMode and enablePreviousImageForDeleteInFFCF true")] public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync() { ContainerInternal monitoredContainer = await this.CreateMonitoredContainer(ChangeFeedMode.AllVersionsAndDeletes); @@ -155,6 +155,8 @@ public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync() ChangeFeedProcessor processor = monitoredContainer .GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes(processorName: "processor", onChangesDelegate: (ChangeFeedProcessorContext context, IReadOnlyCollection> docs, CancellationToken token) => { + string metadataId = default; + string metadataPk = default; string id = default; string pk = default; string description = default; @@ -171,14 +173,13 @@ public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync() } else { - id = change.Previous.id.ToString(); - pk = change.Previous.pk.ToString(); - description = change.Previous.description.ToString(); + metadataId = change.Metadata.Id.ToString(); + metadataPk = change.Metadata.PartitionKey.Values.FirstOrDefault().ToString(); } ChangeFeedOperationType operationType = change.Metadata.OperationType; long previousLsn = change.Metadata.PreviousLsn; - DateTime m = change.Metadata.ConflictResolutionTimestamp; + DateTime? m = change.Metadata.ConflictResolutionTimestamp; long lsn = change.Metadata.Lsn; bool isTimeToLiveExpired = change.Metadata.IsTimeToLiveExpired; } @@ -211,8 +212,9 @@ public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync() ChangeFeedItem deleteChange = docs.ElementAt(2); Assert.IsNull(deleteChange.Current.id); + Assert.AreEqual(expected: "1", actual: deleteChange.Metadata.Id.ToString()); + Assert.AreEqual(expected: "1", actual: deleteChange.Metadata.PartitionKey.Values.FirstOrDefault()); Assert.AreEqual(expected: deleteChange.Metadata.OperationType, actual: ChangeFeedOperationType.Delete); - Assert.AreEqual(expected: replaceChange.Metadata.Lsn, actual: deleteChange.Metadata.PreviousLsn); Assert.IsNotNull(deleteChange.Previous); Assert.AreEqual(expected: "1", actual: deleteChange.Previous.id.ToString()); Assert.AreEqual(expected: "1", actual: deleteChange.Previous.pk.ToString()); @@ -639,5 +641,106 @@ public async Task WhenACFPInAVADModeUsesWithStartFromBeginningExpectExceptionTes expected: "Using the 'WithStartFromBeginning' option with ChangeFeedProcessor is not supported with Microsoft.Azure.Cosmos.ChangeFeed.ChangeFeedModeFullFidelity mode.", actual: exception.Message); } + + [TestMethod] + [Owner("trivediyash")] + [Description("Validates that ConflictResolutionTimestampInSeconds getter throws JsonException when value is zero.")] + public void ValidateConflictResolutionTimestampInSecondsGetterThrowsOnZeroTest() + { + ChangeFeedMetadata metadata = new() + { + Lsn = 374, + OperationType = ChangeFeedOperationType.Create + }; + + // Accessing the getter should throw JsonException when the value is zero (default) + System.Text.Json.JsonException exception = Assert.ThrowsException(() => + { + double value = metadata.ConflictResolutionTimestampInSeconds; + }); + + Assert.IsTrue(exception.Message.Contains("crts")); + Assert.IsTrue(exception.Message.Contains("not set")); + } + + [TestMethod] + [Owner("trivediyash")] + [Description("Validates that ConflictResolutionTimestampInSeconds setter throws JsonException when attempting to set zero.")] + public void ValidateConflictResolutionTimestampInSecondsSetterThrowsOnZeroTest() + { + ChangeFeedMetadata metadata = new() + { + Lsn = 374, + OperationType = ChangeFeedOperationType.Create + }; + + // Setting the value to zero should throw JsonException + System.Text.Json.JsonException exception = Assert.ThrowsException(() => + { + metadata.ConflictResolutionTimestampInSeconds = 0; + }); + + Assert.IsTrue(exception.Message.Contains("crts")); + Assert.IsTrue(exception.Message.Contains("cannot be zero")); + } + + [TestMethod] + [Owner("trivediyash")] + [Description("Validates that deserializing ChangeFeedMetadata without crts field or with negative crts throws JsonException when accessing ConflictResolutionTimestampInSeconds.")] + public void ValidateDeserializationWithoutCrtsFieldThrowsOnAccessTest() + { + // JSON without the "crts" field - this should deserialize but accessing ConflictResolutionTimestampInSeconds should throw + string jsonWithoutCrts = @"{ + ""lsn"": 374, + ""operationType"": ""Create"", + ""previousImageLSN"": 0, + ""timeToLiveExpired"": false + }"; + + // Deserialize the JSON - this should succeed + ChangeFeedMetadata metadata = System.Text.Json.JsonSerializer.Deserialize(jsonWithoutCrts); + + Assert.IsNotNull(metadata); + Assert.AreEqual(expected: 374, actual: metadata.Lsn); + Assert.AreEqual(expected: ChangeFeedOperationType.Create, actual: metadata.OperationType); + + // Accessing ConflictResolutionTimestampInSeconds should throw because the value is zero (not set) + System.Text.Json.JsonException exception = Assert.ThrowsException(() => + { + double value = metadata.ConflictResolutionTimestampInSeconds; + }); + + Assert.IsTrue(exception.Message.Contains("crts")); + Assert.IsTrue(exception.Message.Contains("not set")); + + // Accessing ConflictResolutionTimestamp should also throw since it uses ConflictResolutionTimestampInSeconds + System.Text.Json.JsonException timestampException = Assert.ThrowsException(() => + { + DateTime timestamp = metadata.ConflictResolutionTimestamp; + }); + + Assert.IsTrue(timestampException.Message.Contains("crts")); + Assert.IsTrue(timestampException.Message.Contains("not set")); + + // Test with negative value - deserialize JSON with negative crts + // The setter only validates == 0, so negative values can be deserialized + // But the getter will throw when accessed + string jsonWithNegativeCrts = @"{ + ""lsn"": 374, + ""crts"": -100, + ""operationType"": ""Create"" + }"; + + ChangeFeedMetadata metadataWithNegative = System.Text.Json.JsonSerializer.Deserialize(jsonWithNegativeCrts); + + // Accessing the getter should throw JsonException when the value is negative + System.Text.Json.JsonException negativeException = Assert.ThrowsException(() => + { + double value = metadataWithNegative.ConflictResolutionTimestampInSeconds; + }); + + Assert.IsTrue(negativeException.Message.Contains("crts")); + Assert.IsTrue(negativeException.Message.Contains("not set")); + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/BuilderWithCustomSerializerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/BuilderWithCustomSerializerTests.cs index a6780e4409..a89d5e2c1c 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/BuilderWithCustomSerializerTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/BuilderWithCustomSerializerTests.cs @@ -24,7 +24,28 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests.CFP.AllVersionsAndDeletes [TestCategory("ChangeFeedProcessor")] public class BuilderWithCustomSerializerTests { - [TestMethod] + internal CosmosClient cosmosClient; + internal Database database; + + [TestCleanup] + public async Task Cleanup() + { + try + { + if (this.database != null) + { + await this.database.DeleteAsync(); + } + } + catch (Exception) + { + // Ignore exceptions during cleanup + } + + this.cosmosClient?.Dispose(); + } + + [TestMethod] [Owner("philipthomas")] [Description("Validating to deserization of ChangeFeedItem with a Delete payload with TimeToLiveExpired set to true.")] [DataRow(true)] @@ -39,7 +60,11 @@ public void ValidateNSJAndSTJSerializationOfChangeFeedItemDeleteTimeToLiveExpire ""crts"": 1722511591, ""operationType"": ""delete"", ""timeToLiveExpired"": true, - ""previousImageLSN"": 16 + ""previousImageLSN"": 16, + ""id"": ""1"", + ""partitionKey"": { + ""pk"": ""1"" + } }, ""previous"": { ""id"": ""1"", @@ -92,6 +117,8 @@ static void ValidateDeserialization(List> activitie Assert.IsTrue(deletedChange.Metadata.IsTimeToLiveExpired); Assert.IsNotNull(deletedChange.Previous); Assert.AreEqual(expected: "Testing TTL on CFP.", actual: deletedChange.Previous.description); + Assert.AreEqual(expected: "1", actual: deletedChange.Metadata.Id.ToString()); + Assert.AreEqual(expected: "1", actual: deletedChange.Metadata.PartitionKey.Values.FirstOrDefault().ToString()); Assert.AreEqual(expected: "1", actual: deletedChange.Previous.id); Assert.AreEqual(expected: 5, actual: deletedChange.Previous.ttl); } @@ -216,7 +243,11 @@ public void ValidateNSJAndSTJSerializationOfChangeFeedItemTest(bool propertyName ""lsn"": 376, ""operationType"": ""delete"", ""previousImageLSN"": 375, - ""timeToLiveExpired"": false + ""timeToLiveExpired"": false, + ""id"": ""1"", + ""partitionKey"": { + ""pk"": ""1"" + } }, ""previous"": { ""id"": ""1"", @@ -295,11 +326,173 @@ static void ValidateDeserialization(List> activitie Assert.IsFalse(deletedChange.Metadata.IsTimeToLiveExpired); Assert.IsNotNull(deletedChange.Previous); Assert.AreEqual(expected: "test after replace", actual: deletedChange.Previous.description); + Assert.AreEqual(expected: "1", actual: deletedChange.Metadata.Id.ToString()); + Assert.AreEqual(expected: "1", actual: deletedChange.Metadata.PartitionKey.Values.FirstOrDefault().ToString()); Assert.AreEqual(expected: "1", actual: deletedChange.Previous.id); Assert.AreEqual(expected: 0, actual: deletedChange.Previous.ttl); } } + [TestMethod] + [Owner("trivediyash")] + [Description("Validating to deserization using NSJ and STJ of ChangeFeedItem with HPK (Hierarchical Partition Key) with Create, Replace, and Delete payload.")] + [DataRow(true)] + [DataRow(false)] + public void ValidateNSJAndSTJSerializationOfChangeFeedItemWithHPKTest(bool propertyNameCaseInsensitive) + { + string json = @"[ + { + ""current"": { + ""id"": ""1"", + ""pk1"": ""value1"", + ""pk2"": ""value2"", + ""pk3"": ""value3"", + ""description"": ""original test with HPK"", + ""_rid"": ""HpxDAL+dzLQBAAAAAAAAAA=="", + ""_self"": ""dbs/HpxDAA==/colls/HpxDAL+dzLQ=/docs/HpxDAL+dzLQBAAAAAAAAAA==/"", + ""_etag"": ""\""00000000-0000-0000-e384-28095c1a01da\"""", + ""_attachments"": ""attachments/"", + ""_ts"": 1722455970 + }, + ""metadata"": { + ""crts"": 1722455970, + ""lsn"": 374, + ""operationType"": ""create"", + ""previousImageLSN"": 0, + ""timeToLiveExpired"": false + } + }, + { + ""current"": { + ""id"": ""1"", + ""pk1"": ""value1"", + ""pk2"": ""value2"", + ""pk3"": ""value3"", + ""description"": ""test after replace with HPK"", + ""_rid"": ""HpxDAL+dzLQBAAAAAAAAAA=="", + ""_self"": ""dbs/HpxDAA==/colls/HpxDAL+dzLQ=/docs/HpxDAL+dzLQBAAAAAAAAAA==/"", + ""_etag"": ""\""00000000-0000-0000-e384-28a5abdd01da\"""", + ""_attachments"": ""attachments/"", + ""_ts"": 1722455971 + }, + ""metadata"": { + ""crts"": 1722455971, + ""lsn"": 375, + ""operationType"": ""replace"", + ""previousImageLSN"": 374, + ""timeToLiveExpired"": false + } + }, + { + ""current"": {}, + ""metadata"": { + ""crts"": 1722455972, + ""lsn"": 376, + ""operationType"": ""delete"", + ""previousImageLSN"": 375, + ""timeToLiveExpired"": false, + ""id"": ""1"", + ""partitionKey"": { + ""pk1"": ""value1"", + ""pk2"": ""value2"", + ""pk3"": ""value3"" + } + }, + ""previous"": { + ""id"": ""1"", + ""pk1"": ""value1"", + ""pk2"": ""value2"", + ""pk3"": ""value3"", + ""description"": ""test after replace with HPK"", + ""_rid"": ""HpxDAL+dzLQBAAAAAAAAAA=="", + ""_self"": ""dbs/HpxDAA==/colls/HpxDAL+dzLQ=/docs/HpxDAL+dzLQBAAAAAAAAAA==/"", + ""_etag"": ""\""00000000-0000-0000-e384-28a5abdd01da\"""", + ""_attachments"": ""attachments/"", + ""_ts"": 1722455971 + } + } + ]"; + + ValidateSystemTextJsonDeserialization(json, propertyNameCaseInsensitive); + ValidateNewtonsoftJsonDeserialization(json); + + static void ValidateNewtonsoftJsonDeserialization(string json) + { + ValidateDeserialization(JsonConvert.DeserializeObject>>(json)); + } + + static void ValidateSystemTextJsonDeserialization(string json, bool propertyNameCaseInsensitive) + { + ValidateDeserialization(System.Text.Json.JsonSerializer.Deserialize>>( + json: json, + options: new JsonSerializerOptions() + { + PropertyNameCaseInsensitive = propertyNameCaseInsensitive + })); + } + + static void ValidateDeserialization(List> activities) + { + Assert.IsNotNull(activities); + + ChangeFeedItem createdUpdate = activities.ElementAt(0); + Assert.IsNotNull(createdUpdate); + Assert.IsNotNull(createdUpdate.Current); + Assert.AreEqual(expected: "original test with HPK", actual: createdUpdate.Current.description); + Assert.AreEqual(expected: "1", actual: createdUpdate.Current.id); + Assert.AreEqual(expected: "value1", actual: createdUpdate.Current.pk1); + Assert.AreEqual(expected: "value2", actual: createdUpdate.Current.pk2); + Assert.AreEqual(expected: "value3", actual: createdUpdate.Current.pk3); + Assert.IsNotNull(createdUpdate.Metadata); + Assert.AreEqual(expected: DateTime.Parse("7/31/2024 7:59:30 PM"), actual: createdUpdate.Metadata.ConflictResolutionTimestamp); + Assert.AreEqual(expected: 374, actual: createdUpdate.Metadata.Lsn); + Assert.AreEqual(expected: ChangeFeedOperationType.Create, actual: createdUpdate.Metadata.OperationType); + Assert.AreEqual(expected: 0, actual: createdUpdate.Metadata.PreviousLsn); + Assert.IsFalse(createdUpdate.Metadata.IsTimeToLiveExpired); + Assert.IsNull(createdUpdate.Previous); // No Previous for a Create change. + + ChangeFeedItem replacedChange = activities.ElementAt(1); + Assert.IsNotNull(replacedChange); + Assert.IsNotNull(replacedChange.Current); + Assert.AreEqual(expected: "test after replace with HPK", actual: replacedChange.Current.description); + Assert.AreEqual(expected: "1", actual: replacedChange.Current.id); + Assert.AreEqual(expected: "value1", actual: replacedChange.Current.pk1); + Assert.AreEqual(expected: "value2", actual: replacedChange.Current.pk2); + Assert.AreEqual(expected: "value3", actual: replacedChange.Current.pk3); + Assert.IsNotNull(replacedChange.Metadata); + Assert.AreEqual(expected: DateTime.Parse("7/31/2024 7:59:31 PM"), actual: replacedChange.Metadata.ConflictResolutionTimestamp); + Assert.AreEqual(expected: 375, actual: replacedChange.Metadata.Lsn); + Assert.AreEqual(expected: ChangeFeedOperationType.Replace, actual: replacedChange.Metadata.OperationType); + Assert.AreEqual(expected: 374, actual: replacedChange.Metadata.PreviousLsn); + Assert.IsFalse(replacedChange.Metadata.IsTimeToLiveExpired); + Assert.IsNull(replacedChange.Previous); // No Previous for a Replace change. + + ChangeFeedItem deletedChange = activities.ElementAt(2); + Assert.IsNotNull(deletedChange); + Assert.IsNotNull(deletedChange.Current); // Current is not null, but no data. + Assert.AreEqual(expected: default, actual: deletedChange.Current.description); // No current description for Delete + Assert.AreEqual(expected: default, actual: deletedChange.Current.id); // No current id for Delete + Assert.IsNotNull(deletedChange.Metadata); + Assert.AreEqual(expected: DateTime.Parse("7/31/2024 7:59:32 PM"), actual: deletedChange.Metadata.ConflictResolutionTimestamp); + Assert.AreEqual(expected: 376, actual: deletedChange.Metadata.Lsn); + Assert.AreEqual(expected: ChangeFeedOperationType.Delete, actual: deletedChange.Metadata.OperationType); + Assert.AreEqual(expected: 375, actual: deletedChange.Metadata.PreviousLsn); + Assert.IsFalse(deletedChange.Metadata.IsTimeToLiveExpired); + Assert.IsNotNull(deletedChange.Previous); + Assert.AreEqual(expected: "test after replace with HPK", actual: deletedChange.Previous.description); + Assert.AreEqual(expected: "1", actual: deletedChange.Metadata.Id.ToString()); + Assert.IsNotNull(deletedChange.Metadata.PartitionKey); + Assert.AreEqual(expected: 3, actual: deletedChange.Metadata.PartitionKey.Count); + Assert.AreEqual(expected: "value1", actual: deletedChange.Metadata.PartitionKey["pk1"].ToString()); + Assert.AreEqual(expected: "value2", actual: deletedChange.Metadata.PartitionKey["pk2"].ToString()); + Assert.AreEqual(expected: "value3", actual: deletedChange.Metadata.PartitionKey["pk3"].ToString()); + Assert.AreEqual(expected: "1", actual: deletedChange.Previous.id); + Assert.AreEqual(expected: "value1", actual: deletedChange.Previous.pk1); + Assert.AreEqual(expected: "value2", actual: deletedChange.Previous.pk2); + Assert.AreEqual(expected: "value3", actual: deletedChange.Previous.pk3); + } + } + [TestMethod] [Owner("philipthomas-MSFT")] [Description("Replace and Deletes have full ChangeFeedMetadata.")] @@ -313,18 +506,19 @@ public void ValidateChangeFeedMetadataSerializationReplaceAnDeleteWriteTest(bool Lsn = 374, OperationType = ChangeFeedOperationType.Create, IsTimeToLiveExpired = true, - ConflictResolutionTimestamp = DateTime.Parse("7/31/2024 7:59:30 PM") + ConflictResolutionTimestampInSeconds = 1722455970 }; string json = System.Text.Json.JsonSerializer.Serialize( value: metadata, options: new JsonSerializerOptions { - PropertyNameCaseInsensitive = propertyNameCaseInsensitive + PropertyNameCaseInsensitive = propertyNameCaseInsensitive, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); Assert.AreEqual( - expected: @"{""crts"":1722455970,""timeToLiveExpired"":true,""lsn"":374,""operationType"":""Create"",""previousImageLSN"":15}", + expected: @"{""crts"":1722455970,""lsn"":374,""operationType"":""Create"",""previousImageLSN"":15,""timeToLiveExpired"":true}", actual: json); } @@ -339,21 +533,121 @@ public void ValidateChangeFeedMetadataSerializationCreateWriteTest(bool property { Lsn = 374, OperationType = ChangeFeedOperationType.Create, - ConflictResolutionTimestamp = DateTime.Parse("7/31/2024 7:59:30 PM") + ConflictResolutionTimestampInSeconds = 1722455970 }; string json = System.Text.Json.JsonSerializer.Serialize( value: metadata, options: new JsonSerializerOptions { - PropertyNameCaseInsensitive = propertyNameCaseInsensitive + PropertyNameCaseInsensitive = propertyNameCaseInsensitive, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); Assert.AreEqual( - expected: @"{""crts"":1722455970,""timeToLiveExpired"":false,""lsn"":374,""operationType"":""Create"",""previousImageLSN"":0}", + expected: @"{""crts"":1722455970,""lsn"":374,""operationType"":""Create"",""previousImageLSN"":0,""timeToLiveExpired"":false}", actual: json); } + [TestMethod] + [Owner("trivediyash")] + [Description("Validates that ConflictResolutionTimestampInSeconds getter throws JsonException when value is zero or negative.")] + public void ValidateConflictResolutionTimestampInSecondsGetterThrowsOnZeroTest() + { + ChangeFeedMetadata metadata = new() + { + Lsn = 374, + OperationType = ChangeFeedOperationType.Create + }; + + // Accessing the getter should throw JsonException when the value is zero (default) + System.Text.Json.JsonException exception = Assert.ThrowsException(() => + { + double value = metadata.ConflictResolutionTimestampInSeconds; + }); + + Assert.IsTrue(exception.Message.Contains("crts")); + Assert.IsTrue(exception.Message.Contains("not set")); + + // Test with negative value - deserialize JSON with negative crts + string jsonWithNegativeCrts = @"{ + ""lsn"": 374, + ""crts"": -100, + ""operationType"": ""Create"" + }"; + + ChangeFeedMetadata metadataWithNegative = System.Text.Json.JsonSerializer.Deserialize(jsonWithNegativeCrts); + + // Accessing the getter should throw JsonException when the value is negative + System.Text.Json.JsonException negativeException = Assert.ThrowsException(() => + { + double value = metadataWithNegative.ConflictResolutionTimestampInSeconds; + }); + + Assert.IsTrue(negativeException.Message.Contains("crts")); + Assert.IsTrue(negativeException.Message.Contains("not set")); + } + + [TestMethod] + [Owner("trivediyash")] + [Description("Validates that ConflictResolutionTimestampInSeconds setter throws JsonException when attempting to set zero.")] + public void ValidateConflictResolutionTimestampInSecondsSetterThrowsOnZeroTest() + { + ChangeFeedMetadata metadata = new() + { + Lsn = 374, + OperationType = ChangeFeedOperationType.Create + }; + + // Setting the value to zero should throw JsonException + System.Text.Json.JsonException exception = Assert.ThrowsException(() => + { + metadata.ConflictResolutionTimestampInSeconds = 0; + }); + + Assert.IsTrue(exception.Message.Contains("crts")); + Assert.IsTrue(exception.Message.Contains("cannot be zero")); + } + + [TestMethod] + [Owner("trivediyash")] + [Description("Validates that deserializing ChangeFeedMetadata without crts field throws JsonException when accessing ConflictResolutionTimestampInSeconds.")] + public void ValidateDeserializationWithoutCrtsFieldThrowsOnAccessTest() + { + // JSON without the "crts" field - this should deserialize but accessing ConflictResolutionTimestampInSeconds should throw + string jsonWithoutCrts = @"{ + ""lsn"": 374, + ""operationType"": ""Create"", + ""previousImageLSN"": 0, + ""timeToLiveExpired"": false + }"; + + // Deserialize the JSON - this should succeed + ChangeFeedMetadata metadata = System.Text.Json.JsonSerializer.Deserialize(jsonWithoutCrts); + + Assert.IsNotNull(metadata); + Assert.AreEqual(expected: 374, actual: metadata.Lsn); + Assert.AreEqual(expected: ChangeFeedOperationType.Create, actual: metadata.OperationType); + + // Accessing ConflictResolutionTimestampInSeconds should throw because the value is zero (not set) + System.Text.Json.JsonException exception = Assert.ThrowsException(() => + { + double value = metadata.ConflictResolutionTimestampInSeconds; + }); + + Assert.IsTrue(exception.Message.Contains("crts")); + Assert.IsTrue(exception.Message.Contains("not set")); + + // Accessing ConflictResolutionTimestamp should also throw since it uses ConflictResolutionTimestampInSeconds + System.Text.Json.JsonException timestampException = Assert.ThrowsException(() => + { + DateTime timestamp = metadata.ConflictResolutionTimestamp; + }); + + Assert.IsTrue(timestampException.Message.Contains("crts")); + Assert.IsTrue(timestampException.Message.Contains("not set")); + } + [TestMethod] [Timeout(300000)] [TestCategory("LongRunning")] @@ -376,7 +670,7 @@ public async Task WhenADocumentIsCreatedWithTtlSetThenTheDocumentIsDeletedTestsA Container leaseContainer = await database.CreateContainerIfNotExistsAsync(containerProperties: new ContainerProperties(id: "leases", partitionKeyPath: "/id")); ContainerInternal monitoredContainer = await this.CreateMonitoredContainer(ChangeFeedMode.AllVersionsAndDeletes, database); Exception exception = default; - int ttlInSeconds = 5; + int ttlInSeconds = 1; Stopwatch stopwatch = new(); ManualResetEvent allDocsProcessed = new ManualResetEvent(false); @@ -401,6 +695,8 @@ public async Task WhenADocumentIsCreatedWithTtlSetThenTheDocumentIsDeletedTestsA Assert.IsTrue(DateTime.TryParse(s: change.Metadata.ConflictResolutionTimestamp.ToString(), out _), message: "Invalid csrt must be a datetime value."); Assert.IsTrue(change.Metadata.Lsn > 0, message: "Invalid lsn must be a long value."); Assert.IsFalse(change.Metadata.IsTimeToLiveExpired); + Assert.IsNull(change.Metadata.Id); + Assert.IsNull(change.Metadata.PartitionKey); // previous Assert.IsNull(change.Previous); @@ -414,12 +710,11 @@ public async Task WhenADocumentIsCreatedWithTtlSetThenTheDocumentIsDeletedTestsA Assert.IsTrue(DateTime.TryParse(s: change.Metadata.ConflictResolutionTimestamp.ToString(), out _), message: "Invalid csrt must be a datetime value."); Assert.IsTrue(change.Metadata.Lsn > 0, message: "Invalid lsn must be a long value."); Assert.IsTrue(change.Metadata.IsTimeToLiveExpired); + Assert.AreEqual(expected: "1", actual: change.Metadata.Id.ToString()); + Assert.AreEqual(expected: "1", actual: change.Metadata.PartitionKey.Values.FirstOrDefault()); // previous - Assert.AreEqual(expected: "1", actual: change.Previous.id.ToString()); - Assert.AreEqual(expected: "1", actual: change.Previous.pk.ToString()); - Assert.AreEqual(expected: "Testing TTL on CFP.", actual: change.Previous.description.ToString()); - Assert.AreEqual(expected: ttlInSeconds, actual: change.Previous.ttl); + Assert.IsNull(change.Previous); // stop after reading delete since it is the last document in feed. stopwatch.Stop(); @@ -482,24 +777,51 @@ public async Task WhenADocumentIsCreatedWithTtlSetThenTheDocumentIsDeletedTestsA } [TestMethod] + [TestCategory("ChangeFeed")] [Owner("philipthomas-MSFT")] [Description("Scenario: When a document is created, then updated, and finally deleted, there should be 3 changes that will appear for that " + - "document when using ChangeFeedProcessor with AllVersionsAndDeletes set as the ChangeFeedMode.")] + "document when using ChangeFeedProcessor with AllVersionsAndDeletes set as the ChangeFeedMode. This test runs against the Cosmos DB Emulator" + + " which has enablePreviousImageForDeleteInFFCF set to true.")] [DataRow(true)] [DataRow(false)] - public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync(bool propertyNameCaseInsensitive) + public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsyncEmulator(bool propertyNameCaseInsensitive) { - CosmosClient cosmosClient = TestCommon.CreateCosmosClient((cosmosClientBuilder) => - cosmosClientBuilder.WithSystemTextJsonSerializerOptions( - new JsonSerializerOptions() - { - PropertyNameCaseInsensitive = propertyNameCaseInsensitive - }), - useCustomSeralizer: false); + await this.WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync(propertyNameCaseInsensitive); + } + + [TestMethod] + [TestCategory("MultiMaster")] + [Owner("philipthomas-MSFT")] + [Description("Scenario: When a document is created, then updated, and finally deleted, there should be 3 changes that will appear for that " + + "document when using ChangeFeedProcessor with AllVersionsAndDeletes set as the ChangeFeedMode. This test runs against a live multi-region" + + " Cosmos DB account which has does not have enablePreviousImageForDeleteInFFCF set.")] + [DataRow(true)] + [DataRow(false)] + public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsyncLiveAccount(bool propertyNameCaseInsensitive) + { + await this.WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync(propertyNameCaseInsensitive, true); + } - Database database = await cosmosClient.CreateDatabaseIfNotExistsAsync(id: Guid.NewGuid().ToString()); - Container leaseContainer = await database.CreateContainerIfNotExistsAsync(containerProperties: new ContainerProperties(id: "leases", partitionKeyPath: "/id")); - ContainerInternal monitoredContainer = await this.CreateMonitoredContainer(ChangeFeedMode.AllVersionsAndDeletes, database); + private async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync(bool propertyNameCaseInsensitive, bool isMultiMaster = false) + { + (string defaultEndpoint, string authKey) = TestCommon.GetAccountInfo(); + string accountEndpoint = TestCommon.GetMultiRegionConnectionString(); + + CosmosClientOptions options = new CosmosClientOptions() + { + UseSystemTextJsonSerializerWithOptions = new JsonSerializerOptions() + { + PropertyNameCaseInsensitive = propertyNameCaseInsensitive + } + }; + + this.cosmosClient = isMultiMaster + ? new CosmosClient(accountEndpoint, options) + : new CosmosClient(defaultEndpoint, authKey, options); + + this.database = await this.cosmosClient.CreateDatabaseIfNotExistsAsync(id: Guid.NewGuid().ToString()); + Container leaseContainer = await this.database.CreateContainerIfNotExistsAsync(containerProperties: new ContainerProperties(id: "leases", partitionKeyPath: "/id")); + ContainerInternal monitoredContainer = await this.CreateMonitoredContainer(ChangeFeedMode.AllVersionsAndDeletes, this.database); ManualResetEvent allDocsProcessed = new ManualResetEvent(false); Exception exception = default; @@ -508,6 +830,8 @@ public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync(bool pr { Logger.LogLine($"@ {DateTime.Now}, {nameof(docs)} -> {System.Text.Json.JsonSerializer.Serialize(docs)}"); + string metadataId = default; + string metadataPk = default; string id = default; string pk = default; string description = default; @@ -522,14 +846,13 @@ public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync(bool pr } else { - id = change.Previous.id.ToString(); - pk = change.Previous.pk.ToString(); - description = change.Previous.description.ToString(); + metadataId = change.Metadata.Id.ToString(); + metadataPk = change.Metadata.PartitionKey.Values.FirstOrDefault().ToString(); } ChangeFeedOperationType operationType = change.Metadata.OperationType; long previousLsn = change.Metadata.PreviousLsn; - DateTime m = change.Metadata.ConflictResolutionTimestamp; + DateTime? m = change.Metadata.ConflictResolutionTimestamp; long lsn = change.Metadata.Lsn; bool isTimeToLiveExpired = change.Metadata.IsTimeToLiveExpired; } @@ -564,10 +887,18 @@ public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync(bool pr Assert.IsNull(deleteChange.Current.id); Assert.AreEqual(expected: deleteChange.Metadata.OperationType, actual: ChangeFeedOperationType.Delete); Assert.AreEqual(expected: replaceChange.Metadata.Lsn, actual: deleteChange.Metadata.PreviousLsn); - Assert.IsNotNull(deleteChange.Previous); - Assert.AreEqual(expected: "1", actual: deleteChange.Previous.id.ToString()); - Assert.AreEqual(expected: "1", actual: deleteChange.Previous.pk.ToString()); - Assert.AreEqual(expected: "test after replace", actual: deleteChange.Previous.description.ToString()); + + if (isMultiMaster) + { + Assert.IsNull(deleteChange.Previous); + } + else + { + Assert.IsNotNull(deleteChange.Previous); + Assert.AreEqual(expected: "1", actual: deleteChange.Previous.id.ToString()); + } + Assert.AreEqual(expected: "1", actual: deleteChange.Metadata.Id.ToString()); + Assert.AreEqual(expected: "1", actual: deleteChange.Metadata.PartitionKey.Values.FirstOrDefault().ToString()); Assert.IsTrue(condition: createChange.Metadata.ConflictResolutionTimestamp < replaceChange.Metadata.ConflictResolutionTimestamp, message: "The create operation must happen before the replace operation."); Assert.IsTrue(condition: replaceChange.Metadata.ConflictResolutionTimestamp < deleteChange.Metadata.ConflictResolutionTimestamp, message: "The replace operation must happen before the delete operation."); @@ -609,12 +940,10 @@ public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedTestsAsync(bool pr Assert.Fail(exception.ToString()); } - if (database != null) + if (this.database != null) { - await database.DeleteAsync(); + await this.database.DeleteAsync(); } - - cosmosClient?.Dispose(); } private static async Task RevertLeaseDocumentsToLegacyWithNoMode( @@ -736,7 +1065,7 @@ private async Task CreateMonitoredContainer( if (changeFeedMode == ChangeFeedMode.AllVersionsAndDeletes) { - properties.ChangeFeedPolicy.FullFidelityRetention = TimeSpan.FromMinutes(5); + //properties.ChangeFeedPolicy.FullFidelityRetention = TimeSpan.FromMinutes(5); properties.DefaultTimeToLive = -1; } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/ToDoActivity.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/ToDoActivity.cs index 1058c8f3fe..d8c6df721a 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/ToDoActivity.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CFP/AllVersionsAndDeletes/ToDoActivity.cs @@ -15,4 +15,20 @@ public class ToDoActivity public int ttl { get; set; } } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Used for CFP AllVersionsAndDeletes builder tests with HPK without having attribute annotations from STJ or NSJ.")] + public class ToDoActivityWithHPK + { + public string id { get; set; } + + public string pk1 { get; set; } + + public string pk2 { get; set; } + + public string pk3 { get; set; } + + public string description { get; set; } + + public int ttl { get; set; } + } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAadTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAadTests.cs index 40e9a27e90..1e53f82a44 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAadTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAadTests.cs @@ -13,7 +13,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests using System.Web; using Documents.Client; using global::Azure; - using global::Azure.Core; + using global::Azure.Core; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.IdentityModel.Tokens; using static Microsoft.Azure.Cosmos.SDK.EmulatorTests.TransportClientHelper; @@ -263,6 +263,185 @@ void GetAadTokenCallBack( Assert.IsTrue(ce.ToString().Contains(errorMessage)); } } - } + } + + [TestMethod] + public async Task Aad_OverrideScope_NoFallback_OnFailure_E2E() + { + // Arrange + (string endpoint, string authKey) = TestCommon.GetAccountInfo(); + string databaseId = "db-" + Guid.NewGuid(); + using (CosmosClient setupClient = TestCommon.CreateCosmosClient()) + { + await setupClient.CreateDatabaseAsync(databaseId); + } + + string overrideScope = "https://override/.default"; + string accountScope = $"https://{new Uri(endpoint).Host}/.default"; + int overrideScopeCount = 0; + int accountScopeCount = 0; + + string previous = Environment.GetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE"); + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", overrideScope); + + void GetAadTokenCallBack(TokenRequestContext context, CancellationToken token) + { + string scope = context.Scopes[0]; + if (scope == overrideScope) + { + overrideScopeCount++; + throw new RequestFailedException(408, "Simulated override scope failure"); + } + if (scope == accountScope) + { + accountScopeCount++; + } + } + + LocalEmulatorTokenCredential credential = new LocalEmulatorTokenCredential( + expectedScopes: new[] { overrideScope, accountScope }, + masterKey: authKey, + getTokenCallback: GetAadTokenCallBack); + + CosmosClientOptions clientOptions = new CosmosClientOptions + { + ConnectionMode = ConnectionMode.Gateway, + TokenCredentialBackgroundRefreshInterval = TimeSpan.FromSeconds(60) + }; + + try + { + using CosmosClient aadClient = new CosmosClient(endpoint, credential, clientOptions); + + try + { + // Act + ResponseMessage r = await aadClient.GetDatabase(databaseId).ReadStreamAsync(); + Assert.Fail("Expected failure when override scope token acquisition fails."); + } + catch (RequestFailedException ex) when (ex.Status == (int)HttpStatusCode.RequestTimeout || ex.Status == 408) + { + // Assert + Assert.IsTrue(overrideScopeCount > 0, "Override scope should have been attempted."); + Assert.AreEqual(0, accountScopeCount, "No fallback to account scope must occur when override is configured."); + } + } + finally + { + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", previous); + using CosmosClient cleanup = TestCommon.CreateCosmosClient(); + await cleanup.GetDatabase(databaseId).DeleteAsync(); + } + } + + [TestMethod] + public async Task Aad_AccountScope_Fallbacks_ToCosmosScope() + { + (string endpoint, string authKey) = TestCommon.GetAccountInfo(); + + string previous = Environment.GetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE"); + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", null); + + string accountScope = $"https://{new Uri(endpoint).Host}/.default"; + string aadScope = "https://cosmos.azure.com/.default"; + + int accountScopeCount = 0; + int cosmosScopeCount = 0; + + void GetAadTokenCallBack(TokenRequestContext context, CancellationToken token) + { + string scope = context.Scopes[0]; + + if (string.Equals(scope, accountScope, StringComparison.OrdinalIgnoreCase)) + { + accountScopeCount++; + throw new Exception( + message: "AADSTS500011", + innerException: new Exception("AADSTS500011")); + } + + if (string.Equals(scope, aadScope, StringComparison.OrdinalIgnoreCase)) + { + cosmosScopeCount++; + } + } + + LocalEmulatorTokenCredential credential = new LocalEmulatorTokenCredential( + expectedScopes: new[] { accountScope, aadScope }, + masterKey: authKey, + getTokenCallback: GetAadTokenCallBack); + + CosmosClientOptions clientOptions = new CosmosClientOptions + { + ConnectionMode = ConnectionMode.Gateway, + TokenCredentialBackgroundRefreshInterval = TimeSpan.FromSeconds(60) + }; + + try + { + using CosmosClient aadClient = new CosmosClient(endpoint, credential, clientOptions); + TokenCredentialCache tokenCredentialCache = + ((AuthorizationTokenProviderTokenCredential)aadClient.AuthorizationTokenProvider).tokenCredentialCache; + + string token = await tokenCredentialCache.GetTokenAsync(Tracing.Trace.GetRootTrace("account-fallback-to-cosmos-test")); + Assert.IsFalse(string.IsNullOrEmpty(token), "Fallback should succeed and produce a token."); + + Assert.IsTrue(accountScopeCount >= 1, "Account scope must be attempted first."); + Assert.IsTrue(cosmosScopeCount >= 1, "The client must fall back to cosmos.azure.com scope."); + } + finally + { + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", previous); + } + } + + [TestMethod] + public async Task Aad_AccountScope_Success_NoFallback() + { + // Arrange + (string endpoint, string authKey) = TestCommon.GetAccountInfo(); + + string accountScope = $"https://{new Uri(endpoint).Host}/.default"; + string aadScope = "https://cosmos.azure.com/.default"; + + int accountScopeCount = 0; + int cosmosScopeCount = 0; + + void GetAadTokenCallBack(TokenRequestContext context, CancellationToken token) + { + string scope = context.Scopes[0]; + + if (string.Equals(scope, accountScope, StringComparison.OrdinalIgnoreCase)) + { + accountScopeCount++; + } + + if (string.Equals(scope, aadScope, StringComparison.OrdinalIgnoreCase)) + { + cosmosScopeCount++; + } + } + + LocalEmulatorTokenCredential credential = new LocalEmulatorTokenCredential( + expectedScopes: new[] { accountScope }, + masterKey: authKey, + getTokenCallback: GetAadTokenCallBack); + + CosmosClientOptions clientOptions = new CosmosClientOptions + { + ConnectionMode = ConnectionMode.Gateway, + TokenCredentialBackgroundRefreshInterval = TimeSpan.FromSeconds(60) + }; + + using CosmosClient aadClient = new CosmosClient(endpoint, credential, clientOptions); + TokenCredentialCache tokenCredentialCache = + ((AuthorizationTokenProviderTokenCredential)aadClient.AuthorizationTokenProvider).tokenCredentialCache; + + string token = await tokenCredentialCache.GetTokenAsync(Tracing.Trace.GetRootTrace("account-scope-success-no-fallback")); + Assert.IsFalse(string.IsNullOrEmpty(token), "Token should be acquired successfully with account scope."); + + Assert.AreEqual(1, accountScopeCount, "Account scope must be used exactly once."); + Assert.AreEqual(0, cosmosScopeCount, "Cosmos scope must not be used (no fallback)."); + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAvailabilityStrategyTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAvailabilityStrategyTests.cs index b3b6b7aaaa..2b847ef133 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAvailabilityStrategyTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosAvailabilityStrategyTests.cs @@ -214,7 +214,7 @@ public async Task AvailabilityStrategyNoTriggerTest(bool isPreferredLocationsEmp .Build(), result: FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ResponseDelay) - .WithDelay(TimeSpan.FromMilliseconds(300)) + .WithDelay(TimeSpan.FromMilliseconds(200)) .Build()) .WithDuration(TimeSpan.FromMinutes(90)) .Build(); @@ -228,7 +228,7 @@ public async Task AvailabilityStrategyNoTriggerTest(bool isPreferredLocationsEmp .Build(), result: FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ResponseDelay) - .WithDelay(TimeSpan.FromMilliseconds(3000)) + .WithDelay(TimeSpan.FromMilliseconds(5000)) .Build()) .WithDuration(TimeSpan.FromMinutes(90)) .Build(); @@ -243,7 +243,7 @@ public async Task AvailabilityStrategyNoTriggerTest(bool isPreferredLocationsEmp ConnectionMode = ConnectionMode.Direct, ApplicationPreferredRegions = isPreferredLocationsEmpty ? new List() : new List() { region1, region2 }, AvailabilityStrategy = AvailabilityStrategy.CrossRegionHedgingStrategy( - threshold: TimeSpan.FromMilliseconds(300), + threshold: TimeSpan.FromMilliseconds(150), thresholdStep: TimeSpan.FromMilliseconds(50)), Serializer = this.cosmosSystemTextJsonSerializer }; @@ -267,12 +267,14 @@ public async Task AvailabilityStrategyNoTriggerTest(bool isPreferredLocationsEmp if (isPreferredLocationsEmpty) { Assert.IsTrue(traceDiagnostic.ToString() - .Contains($"\"Hedge Context\":[\"{region1}\",\"{region2}\",\"{region3}\"]")); + .Contains($"\"Hedge Context\":[\"{region1}\",\"{region2}\",\"{region3}\"]"), + $"{traceDiagnostic} does not contain expected regions \"{region1}\", \"{region2}\", \"{region3}\""); } else { Assert.IsTrue(traceDiagnostic.ToString() - .Contains($"\"Hedge Context\":[\"{region1}\",\"{region2}\"]")); + .Contains($"\"Hedge Context\":[\"{region1}\",\"{region2}\"]"), + $"{traceDiagnostic} does not contain expected regions \"{region1}\", \"{region2}\""); } } ; @@ -340,6 +342,71 @@ public async Task AvailabilityStrategyRequestOptionsTriggerTest(bool isPreferred } } + [TestMethod] + [DataRow(false, DisplayName = "ValidateAvailabilityStrategyNoTriggerTest with preferred regions.")] + [DataRow(true, DisplayName = "ValidateAvailabilityStrategyNoTriggerTest w/o preferred regions.")] + [TestCategory("MultiRegion")] + public async Task AvailabilityStrategyResponseRegionDiagnosticsTest(bool isPreferredLocationsEmpty) + { + FaultInjectionRule responseDelay = new FaultInjectionRuleBuilder( + id: "responseDely", + condition: + new FaultInjectionConditionBuilder() + .WithRegion(region1) + .WithOperationType(FaultInjectionOperationType.ReadItem) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ResponseDelay) + .WithDelay(TimeSpan.FromMilliseconds(4000)) + .Build()) + .WithDuration(TimeSpan.FromMinutes(90)) + .Build(); + + List rules = new List() { responseDelay }; + FaultInjector faultInjector = new FaultInjector(rules); + + responseDelay.Disable(); + + CosmosClientOptions clientOptions = new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Direct, + ApplicationPreferredRegions = isPreferredLocationsEmpty ? new List() : new List() { region1, region2 }, + Serializer = this.cosmosSystemTextJsonSerializer + }; + + using (CosmosClient faultInjectionClient = new CosmosClient( + connectionString: this.connectionString, + clientOptions: faultInjector.GetFaultInjectionClientOptions(clientOptions))) + { + Database database = faultInjectionClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + //warm up connections read + ItemResponse _ = await container.ReadItemAsync("testId", new PartitionKey("pk")); + + responseDelay.Enable(); + + ItemRequestOptions requestOptions = new ItemRequestOptions + { + AvailabilityStrategy = new CrossRegionHedgingAvailabilityStrategy( + threshold: TimeSpan.FromMilliseconds(100), + thresholdStep: TimeSpan.FromMilliseconds(50)) + }; + ItemResponse ir = await container.ReadItemAsync( + "testId", + new PartitionKey("pk"), + requestOptions); + + CosmosTraceDiagnostics traceDiagnostic = ir.Diagnostics as CosmosTraceDiagnostics; + Assert.IsNotNull(traceDiagnostic); + Assert.IsTrue(traceDiagnostic.ToString() + .Contains($"\"Hedge Context\":[\"{region1}\",\"{region2}\"")); + traceDiagnostic.Value.Data.TryGetValue("Response Region", out object responseRegionObj); + Assert.IsNotNull(responseRegionObj); + Assert.AreEqual(region2, responseRegionObj as string); + } + } + [TestMethod] [DataRow(false, DisplayName = "ValidateAvailabilityStrategyNoTriggerTest with preferred regions.")] [DataRow(true, DisplayName = "ValidateAvailabilityStrategyNoTriggerTest w/o preferred regions.")] @@ -662,6 +729,8 @@ public async Task AvailabilityStrategyAllFaultsTests(string operation, string co await changeFeedProcessor.StartAsync(); await Task.Delay(1000); + rule.Enable(); + CosmosIntegrationTestObject testObject = new CosmosIntegrationTestObject { Id = "item4", @@ -670,8 +739,6 @@ public async Task AvailabilityStrategyAllFaultsTests(string operation, string co }; await container.UpsertItemAsync(testObject); - rule.Enable(); - await Task.Delay(15000); Assert.IsTrue(rule.GetHitCount() > 0); @@ -1383,6 +1450,90 @@ public async Task HedgingCancellationTokenHandling() } } + [TestMethod] + [TestCategory("MultiMaster")] + public async Task UpsertRequestOptionsTest() + { + FaultInjectionRule responseDelay = new FaultInjectionRuleBuilder( + id: "responseDely", + condition: + new FaultInjectionConditionBuilder() + .WithRegion(region2) + .WithOperationType(FaultInjectionOperationType.Batch) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ResponseDelay) + .WithDelay(TimeSpan.FromMilliseconds(600)) + .Build()) + .WithDuration(TimeSpan.FromMinutes(90)) + .WithHitLimit(2) + .Build(); + + responseDelay.Disable(); + + FaultInjector injector = new FaultInjector(new List() { responseDelay }); + + CosmosClient client = new CosmosClient( + this.connectionString, + new CosmosClientOptions() + { + ApplicationPreferredRegions = new List { region2, region1 }, + AllowBulkExecution = true, + Serializer = this.cosmosSystemTextJsonSerializer, + FaultInjector = injector, + AvailabilityStrategy = AvailabilityStrategy.CrossRegionHedgingStrategy( + threshold: TimeSpan.FromMilliseconds(100), + thresholdStep: TimeSpan.FromMilliseconds(50), + enableMultiWriteRegionHedge: false) + }); + + Container container = client.GetContainer( + MultiRegionSetupHelpers.dbName, + MultiRegionSetupHelpers.containerName); + + _ = await container.ReadContainerAsync(); + + CosmosIntegrationTestObject test = new CosmosIntegrationTestObject() + { + Id = "testId", + Pk = "pk", + Other = "moreInfo" + DateTime.Now.ToString() + }; + + responseDelay.Enable(); + + AvailabilityStrategy writeStrat = AvailabilityStrategy.CrossRegionHedgingStrategy( + TimeSpan.FromMilliseconds(100), + TimeSpan.FromMilliseconds(50), + true); + + ItemRequestOptions options = new ItemRequestOptions() + { + AvailabilityStrategy = writeStrat, + }; + + try + { + ItemResponse ir = await container.UpsertItemAsync( + test, + requestOptions: options); + CosmosTraceDiagnostics traceDiagnostic = ir.Diagnostics as CosmosTraceDiagnostics; + Assert.IsNotNull(traceDiagnostic); + Assert.IsTrue(traceDiagnostic.ToString() + .Contains($"\"Hedge Context\":[\"{region2}\",\"{region1}\"]")); + Assert.IsTrue((int)ir.StatusCode < 400); + } + catch (CosmosException ex) + { + Assert.Fail(ex.Message); + throw; + } + finally + { + responseDelay.Disable(); + } + } + private static async Task HandleChangesAsync( ChangeFeedProcessorContext context, IReadOnlyCollection changes, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosContainerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosContainerTests.cs index 0ba8815f3e..52a46e9264 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosContainerTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosContainerTests.cs @@ -14,11 +14,9 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests using HttpConstants = Microsoft.Azure.Documents.HttpConstants; using Microsoft.Azure.Cosmos.Resource.CosmosExceptions; using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Documents; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; using Newtonsoft.Json.Linq; - using ClientEncryptionIncludedPath = Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath; [TestClass] public class CosmosContainerTests @@ -1336,7 +1334,7 @@ public async Task TimeToLivePropertyPath() [TestMethod] public async Task ContainerCreationFailsWithUnknownClientEncryptionKey() { - Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath unknownKeyConfigured = new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + ClientEncryptionIncludedPath unknownKeyConfigured = new Cosmos.ClientEncryptionIncludedPath() { Path = "/", ClientEncryptionKeyId = "unknownKey", @@ -1344,8 +1342,8 @@ public async Task ContainerCreationFailsWithUnknownClientEncryptionKey() EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", }; - Collection paths = new Collection { unknownKeyConfigured }; - Microsoft.Azure.Cosmos.ClientEncryptionPolicy clientEncryptionPolicyId = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(paths); + Collection paths = new Collection { unknownKeyConfigured }; + Cosmos.ClientEncryptionPolicy clientEncryptionPolicyId = new Cosmos.ClientEncryptionPolicy(paths); ContainerProperties containerProperties = new ContainerProperties(Guid.NewGuid().ToString(), "/PK") { ClientEncryptionPolicy = clientEncryptionPolicyId }; @@ -1370,23 +1368,23 @@ public async Task ClientEncryptionPolicyTest() string containerName = Guid.NewGuid().ToString(); string partitionKeyPath = "/users"; - Collection paths = new Collection() + Collection paths = new Collection() { - new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + new ClientEncryptionIncludedPath() { Path = partitionKeyPath, ClientEncryptionKeyId = "dekId1", EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", EncryptionType = "Deterministic" }, - new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + new ClientEncryptionIncludedPath() { Path = "/id", ClientEncryptionKeyId = "dekId2", EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", EncryptionType = "Deterministic" }, - new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + new ClientEncryptionIncludedPath() { Path = "/path2", ClientEncryptionKeyId = "dekId2", @@ -1399,7 +1397,7 @@ public async Task ClientEncryptionPolicyTest() { Id = containerName, PartitionKey = new Documents.PartitionKeyDefinition() { Paths = new Collection { partitionKeyPath }, Kind = Documents.PartitionKind.Hash }, - ClientEncryptionPolicy = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(includedPaths:paths,policyFormatVersion:2) + ClientEncryptionPolicy = new ClientEncryptionPolicy(includedPaths:paths,policyFormatVersion:2) }; ContainerResponse containerResponse = await this.cosmosDatabase.CreateContainerIfNotExistsAsync(setting); @@ -1408,7 +1406,7 @@ public async Task ClientEncryptionPolicyTest() ContainerProperties responseSettings = containerResponse; Assert.AreEqual(3, responseSettings.ClientEncryptionPolicy.IncludedPaths.Count()); - Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath includedPath = responseSettings.ClientEncryptionPolicy.IncludedPaths.ElementAt(0); + ClientEncryptionIncludedPath includedPath = responseSettings.ClientEncryptionPolicy.IncludedPaths.ElementAt(0); Assert.AreEqual(partitionKeyPath, includedPath.Path); Assert.AreEqual("dekId1", includedPath.ClientEncryptionKeyId); Assert.AreEqual("AEAD_AES_256_CBC_HMAC_SHA256", includedPath.EncryptionAlgorithm); @@ -1433,16 +1431,16 @@ public async Task ClientEncryptionPolicyTest() // version 1 test. containerName = Guid.NewGuid().ToString(); partitionKeyPath = "/users"; - paths = new Collection() + paths = new Collection() { - new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + new ClientEncryptionIncludedPath() { Path = "/path1", ClientEncryptionKeyId = "dekId1", EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", EncryptionType = "Randomized" }, - new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + new ClientEncryptionIncludedPath() { Path = "/path2", ClientEncryptionKeyId = "dekId2", @@ -1455,7 +1453,7 @@ public async Task ClientEncryptionPolicyTest() { Id = containerName, PartitionKey = new Documents.PartitionKeyDefinition() { Paths = new Collection { partitionKeyPath }, Kind = Documents.PartitionKind.Hash }, - ClientEncryptionPolicy = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(paths) + ClientEncryptionPolicy = new ClientEncryptionPolicy(paths) }; containerResponse = await this.cosmosDatabase.CreateContainerIfNotExistsAsync(setting); @@ -1512,9 +1510,9 @@ public async Task ClientEncryptionPolicyFailureTest() { string containerName = Guid.NewGuid().ToString(); string partitionKeyPath = "/users"; - Collection paths = new Collection() + Collection paths = new Collection() { - new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + new ClientEncryptionIncludedPath() { Path = "/path1", ClientEncryptionKeyId = "dekId1", @@ -1528,8 +1526,8 @@ public async Task ClientEncryptionPolicyFailureTest() ContainerProperties setting = new ContainerProperties() { Id = containerName, - PartitionKey = new PartitionKeyDefinition() { Paths = new Collection { partitionKeyPath }, Kind = PartitionKind.Hash }, - ClientEncryptionPolicy = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(paths) + PartitionKey = new Documents.PartitionKeyDefinition() { Paths = new Collection { partitionKeyPath }, Kind = Documents.PartitionKind.Hash }, + ClientEncryptionPolicy = new ClientEncryptionPolicy(paths) }; Assert.Fail("Creating ContainerProperties should have failed."); @@ -1541,7 +1539,7 @@ public async Task ClientEncryptionPolicyFailureTest() try { - Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath path1 = new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + ClientEncryptionIncludedPath path1 = new ClientEncryptionIncludedPath() { Path = "/path1", ClientEncryptionKeyId = "dekId2", @@ -1565,8 +1563,8 @@ public async Task ClientEncryptionPolicyFailureTest() ContainerProperties setting = new ContainerProperties() { Id = containerName, - PartitionKey = new PartitionKeyDefinition() { Paths = new Collection { partitionKeyPath }, Kind = PartitionKind.Hash }, - ClientEncryptionPolicy = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(pathsList) + PartitionKey = new Documents.PartitionKeyDefinition() { Paths = new Collection { partitionKeyPath }, Kind = Documents.PartitionKind.Hash }, + ClientEncryptionPolicy = new ClientEncryptionPolicy(pathsList) }; Assert.Fail("Creating ContainerProperties should have failed."); @@ -1578,16 +1576,16 @@ public async Task ClientEncryptionPolicyFailureTest() try { - Collection pathsToEncryptWithPartitionKey = new Collection() + Collection pathsToEncryptWithPartitionKey = new Collection() { - new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + new ClientEncryptionIncludedPath() { Path = partitionKeyPath, ClientEncryptionKeyId = "dekId1", EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", EncryptionType = "Randomized" }, - new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + new ClientEncryptionIncludedPath() { Path = "/path1", ClientEncryptionKeyId = "dekId1", @@ -1600,7 +1598,7 @@ public async Task ClientEncryptionPolicyFailureTest() { Id = containerName, PartitionKey = new Documents.PartitionKeyDefinition() { Paths = new Collection { partitionKeyPath }, Kind = Documents.PartitionKind.Hash }, - ClientEncryptionPolicy = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(includedPaths: pathsToEncryptWithPartitionKey, policyFormatVersion: 2) + ClientEncryptionPolicy = new ClientEncryptionPolicy(includedPaths: pathsToEncryptWithPartitionKey, policyFormatVersion: 2) }; await this.cosmosDatabase.CreateContainerAsync(setting); @@ -1635,7 +1633,7 @@ public async Task ClientEncryptionPolicyFailureTest() { Id = containerName, PartitionKey = new Documents.PartitionKeyDefinition() { Paths = new Collection { partitionKeyPath }, Kind = Documents.PartitionKind.Hash }, - ClientEncryptionPolicy = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(includedPaths: pathsToEncryptWithPartitionKey, policyFormatVersion: 2) + ClientEncryptionPolicy = new ClientEncryptionPolicy(includedPaths: pathsToEncryptWithPartitionKey, policyFormatVersion: 2) }; await this.cosmosDatabase.CreateContainerAsync(setting); @@ -1649,9 +1647,9 @@ public async Task ClientEncryptionPolicyFailureTest() // failure due to policy format version 1. for Pk and Id try { - Collection pathsToEncryptWithPartitionKey = new Collection() + Collection pathsToEncryptWithPartitionKey = new Collection() { - new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + new ClientEncryptionIncludedPath() { Path = partitionKeyPath, ClientEncryptionKeyId = "dekId1", @@ -1664,7 +1662,7 @@ public async Task ClientEncryptionPolicyFailureTest() { Id = containerName, PartitionKey = new Documents.PartitionKeyDefinition() { Paths = new Collection { partitionKeyPath }, Kind = Documents.PartitionKind.Hash }, - ClientEncryptionPolicy = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(pathsToEncryptWithPartitionKey) + ClientEncryptionPolicy = new ClientEncryptionPolicy(pathsToEncryptWithPartitionKey) }; await this.cosmosDatabase.CreateContainerAsync(setting); @@ -1677,9 +1675,9 @@ public async Task ClientEncryptionPolicyFailureTest() try { - Collection pathsToEncryptWithPartitionKey = new Collection() + Collection pathsToEncryptWithPartitionKey = new Collection() { - new Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath() + new ClientEncryptionIncludedPath() { Path = "/id", ClientEncryptionKeyId = "dekId1", @@ -1692,7 +1690,7 @@ public async Task ClientEncryptionPolicyFailureTest() { Id = containerName, PartitionKey = new Documents.PartitionKeyDefinition() { Paths = new Collection { partitionKeyPath }, Kind = Documents.PartitionKind.Hash }, - ClientEncryptionPolicy = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(pathsToEncryptWithPartitionKey) + ClientEncryptionPolicy = new ClientEncryptionPolicy(pathsToEncryptWithPartitionKey) }; await this.cosmosDatabase.CreateContainerAsync(setting); @@ -1721,7 +1719,7 @@ public async Task ClientEncryptionPolicyFailureTest() { Id = containerName, PartitionKeyPaths = new Collection { "/path1", "/id" }, - ClientEncryptionPolicy = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(includedPaths: pathsToEncryptWithPartitionKey, policyFormatVersion: 2) + ClientEncryptionPolicy = new ClientEncryptionPolicy(includedPaths: pathsToEncryptWithPartitionKey, policyFormatVersion: 2) }; await this.cosmosDatabase.CreateContainerAsync(setting); @@ -1750,7 +1748,7 @@ public async Task ClientEncryptionPolicyFailureTest() { Id = containerName, PartitionKeyPaths = new Collection { partitionKeyPath, "/path1" }, - ClientEncryptionPolicy = new Microsoft.Azure.Cosmos.ClientEncryptionPolicy(includedPaths: pathsToEncryptWithPartitionKey, policyFormatVersion: 2) + ClientEncryptionPolicy = new ClientEncryptionPolicy(includedPaths: pathsToEncryptWithPartitionKey, policyFormatVersion: 2) }; await this.cosmosDatabase.CreateContainerAsync(setting); @@ -1795,7 +1793,7 @@ public async Task ContainerCreationDeletionWithBinaryEncodingTest(bool binaryEnc Assert.AreEqual(containerId, readResponse.Resource.Id); ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity(); - ItemResponse itemResponse = await readContainer.CreateItemAsync(testItem, new Microsoft.Azure.Cosmos.PartitionKey(testItem.pk)); + ItemResponse itemResponse = await readContainer.CreateItemAsync(testItem, new PartitionKey(testItem.pk)); Assert.AreEqual(HttpStatusCode.Created, itemResponse.StatusCode); // Delete the container diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs index eb68823ba0..72942fe9d8 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs @@ -1,162 +1,164 @@ -namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests -{ - using System; +namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests +{ + using System; using System.Collections.Generic; + using System.Diagnostics; using System.IO; - using System.Linq; + using System.Linq; using System.Net; using System.Net.Http; using System.Text; - using System.Text.Json; - using System.Text.Json.Serialization; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.Diagnostics; - using Microsoft.Azure.Cosmos.FaultInjection; + using System.Text.Json; + using System.Text.Json.Serialization; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Diagnostics; + using Microsoft.Azure.Cosmos.FaultInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json.Linq; - using static Microsoft.Azure.Cosmos.Routing.GlobalPartitionEndpointManagerCore; - using static Microsoft.Azure.Cosmos.SDK.EmulatorTests.MultiRegionSetupHelpers; - - [TestClass] - public class CosmosItemIntegrationTests - { - private string connectionString; - private CosmosClient client; - private Database database; - private Container container; - private Container changeFeedContainer; - - private static string region1; - private static string region2; + using static Microsoft.Azure.Cosmos.Routing.GlobalPartitionEndpointManagerCore; + using static Microsoft.Azure.Cosmos.SDK.EmulatorTests.MultiRegionSetupHelpers; + + [TestClass] + public class CosmosItemIntegrationTests + { + private string connectionString; + private CosmosClient client; + private Database database; + private Container container; + private Container changeFeedContainer; + + private static string region1; + private static string region2; private static string region3; - private IDictionary readRegionsMapping; - private IList thinClientreadRegionalEndpoints; - private CosmosSystemTextJsonSerializer cosmosSystemTextJsonSerializer; - - [TestInitialize] - public async Task TestInitAsync() + private IDictionary readRegionsMapping; + private IList thinClientreadRegionalEndpoints; + private CosmosSystemTextJsonSerializer cosmosSystemTextJsonSerializer; + + [TestInitialize] + public async Task TestInitAsync() { this.connectionString = ConfigurationManager.GetEnvironmentVariable("COSMOSDB_MULTI_REGION", null); - - JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions() - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - this.cosmosSystemTextJsonSerializer = new MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer(jsonSerializerOptions); - - if (string.IsNullOrEmpty(this.connectionString)) - { - Assert.Fail("Set environment variable COSMOSDB_MULTI_REGION to run the tests"); - } - this.client = new CosmosClient( - this.connectionString, - new CosmosClientOptions() - { - Serializer = this.cosmosSystemTextJsonSerializer, - }); - - (this.database, this.container, this.changeFeedContainer) = await MultiRegionSetupHelpers.GetOrCreateMultiRegionDatabaseAndContainers(this.client); - - this.readRegionsMapping = this.client.DocumentClient.GlobalEndpointManager.GetAvailableReadEndpointsByLocation(); - Assert.IsTrue(this.readRegionsMapping.Count() >= 3); - - region1 = this.readRegionsMapping.Keys.ElementAt(0); - region2 = this.readRegionsMapping.Keys.ElementAt(1); - region3 = this.readRegionsMapping.Keys.ElementAt(2); - } - - [TestCleanup] - public void TestCleanup() - { - try - { - this.container.DeleteItemAsync("deleteMe", new PartitionKey("MMWrite")); - } - catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) - { - // Ignore - } - finally - { - //Do not delete the resources (except MM Write test object), georeplication is slow and we want to reuse the resources - this.client?.Dispose(); - } - } - - [TestMethod] - [TestCategory("MultiRegion")] - [Timeout(70000)] - public async Task ReadMany2UnreachablePartitionsTest() - { - List feedRanges = (List)await this.container.GetFeedRangesAsync(); - Assert.IsTrue(feedRanges.Count > 0); - - FaultInjectionCondition condition = new FaultInjectionConditionBuilder() - .WithConnectionType(FaultInjectionConnectionType.Direct) - .WithOperationType(FaultInjectionOperationType.QueryItem) - .WithEndpoint(new FaultInjectionEndpointBuilder( - MultiRegionSetupHelpers.dbName, - MultiRegionSetupHelpers.containerName, - feedRanges[0]) - .WithReplicaCount(2) - .WithIncludePrimary(false) - .Build()) - .Build(); - - FaultInjectionServerErrorResult result = new FaultInjectionServerErrorResultBuilder(FaultInjectionServerErrorType.Gone) - .WithTimes(int.MaxValue - 1) - .Build(); - - FaultInjectionRule rule = new FaultInjectionRuleBuilder("connectionDelay", condition, result) - .WithDuration(TimeSpan.FromDays(1)) - .Build(); - - FaultInjector injector = new FaultInjector(new List { rule }); - - rule.Disable(); - - CosmosClientOptions clientOptions = new CosmosClientOptions() - { - ConnectionMode = ConnectionMode.Direct, - ConsistencyLevel = ConsistencyLevel.Strong, - Serializer = this.cosmosSystemTextJsonSerializer, - FaultInjector = injector, - }; - - CosmosClient fiClient = new CosmosClient( - connectionString: this.connectionString, - clientOptions: clientOptions); - - Database fidb = fiClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container fic = fidb.GetContainer(MultiRegionSetupHelpers.containerName); - - IReadOnlyList<(string, PartitionKey)> items = new List<(string, PartitionKey)>() - { - ("testId", new PartitionKey("pk")), - ("testId2", new PartitionKey("pk2")), - ("testId3", new PartitionKey("pk3")), - ("testId4", new PartitionKey("pk4")), - }; - - try - { - rule.Enable(); - FeedResponse feedResponse = await fic.ReadManyItemsAsync(items); - } - catch (Exception ex) - { - Assert.Fail(ex.ToString()); - } - finally - { - rule.Disable(); - fiClient.Dispose(); - } + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + this.cosmosSystemTextJsonSerializer = new MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer(jsonSerializerOptions); + + if (string.IsNullOrEmpty(this.connectionString)) + { + Assert.Fail("Set environment variable COSMOSDB_MULTI_REGION to run the tests"); + } + this.client = new CosmosClient( + this.connectionString, + new CosmosClientOptions() + { + Serializer = this.cosmosSystemTextJsonSerializer, + }); + + (this.database, this.container, this.changeFeedContainer) = await MultiRegionSetupHelpers.GetOrCreateMultiRegionDatabaseAndContainers(this.client); + + this.readRegionsMapping = this.client.DocumentClient.GlobalEndpointManager.GetAvailableReadEndpointsByLocation(); + Assert.IsTrue(this.readRegionsMapping.Count() >= 3); + + region1 = this.readRegionsMapping.Keys.ElementAt(0); + region2 = this.readRegionsMapping.Keys.ElementAt(1); + region3 = this.readRegionsMapping.Keys.ElementAt(2); + } + + [TestCleanup] + public void TestCleanup() + { + try + { + this.container.DeleteItemAsync("deleteMe", new PartitionKey("MMWrite")); + } + catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + // Ignore + } + finally + { + //Do not delete the resources (except MM Write test object), georeplication is slow and we want to reuse the resources + this.client?.Dispose(); + Environment.SetEnvironmentVariable(ConfigurationManager.StalePartitionUnavailabilityRefreshIntervalInSeconds, null); + } + } + + [TestMethod] + [TestCategory("MultiRegion")] + [Timeout(70000)] + public async Task ReadMany2UnreachablePartitionsTest() + { + List feedRanges = (List)await this.container.GetFeedRangesAsync(); + Assert.IsTrue(feedRanges.Count > 0); + + FaultInjectionCondition condition = new FaultInjectionConditionBuilder() + .WithConnectionType(FaultInjectionConnectionType.Direct) + .WithOperationType(FaultInjectionOperationType.QueryItem) + .WithEndpoint(new FaultInjectionEndpointBuilder( + MultiRegionSetupHelpers.dbName, + MultiRegionSetupHelpers.containerName, + feedRanges[0]) + .WithReplicaCount(2) + .WithIncludePrimary(false) + .Build()) + .Build(); + + FaultInjectionServerErrorResult result = new FaultInjectionServerErrorResultBuilder(FaultInjectionServerErrorType.Gone) + .WithTimes(int.MaxValue - 1) + .Build(); + + FaultInjectionRule rule = new FaultInjectionRuleBuilder("connectionDelay", condition, result) + .WithDuration(TimeSpan.FromDays(1)) + .Build(); + + FaultInjector injector = new FaultInjector(new List { rule }); + + rule.Disable(); + + CosmosClientOptions clientOptions = new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Direct, + ConsistencyLevel = ConsistencyLevel.Strong, + Serializer = this.cosmosSystemTextJsonSerializer, + FaultInjector = injector, + }; + + CosmosClient fiClient = new CosmosClient( + connectionString: this.connectionString, + clientOptions: clientOptions); + + Database fidb = fiClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container fic = fidb.GetContainer(MultiRegionSetupHelpers.containerName); + + IReadOnlyList<(string, PartitionKey)> items = new List<(string, PartitionKey)>() + { + ("testId", new PartitionKey("pk")), + ("testId2", new PartitionKey("pk2")), + ("testId3", new PartitionKey("pk3")), + ("testId4", new PartitionKey("pk4")), + }; + + try + { + rule.Enable(); + FeedResponse feedResponse = await fic.ReadManyItemsAsync(items); + } + catch (Exception ex) + { + Assert.Fail(ex.ToString()); + } + finally + { + rule.Disable(); + fiClient.Dispose(); + } } [TestMethod] - [Timeout(70000)] + [Timeout(70000)] [TestCategory("MultiRegion")] public async Task DateTimeArrayRoundtrip_BinaryEncoding_CompareExtraDates_IntegrationTest() { @@ -166,20 +168,20 @@ public async Task DateTimeArrayRoundtrip_BinaryEncoding_CompareExtraDates_Integr string testId = Guid.NewGuid().ToString(); string[] dateStrings = - { - "12/25/2023","2023-12-25","12-25-2023","25.12.2023","25/12/2023", - "Dec 25, 2023","Dec 25 2023","2023-12-25T10:00:00","2023-12-25T10:00:00.123", - "12/25/2023 10:00 AM","12/25/2023 10:00:00 AM","12/25/2023 10:00:00.123 AM","9999-12-31T23:59:59", - "2023-12-25T10:00:00.1","2023-12-25T10:00:00.12", - "2023-12-25T10:00:00.1234","2023-12-25T10:00:00.1234567" + { + "12/25/2023","2023-12-25","12-25-2023","25.12.2023","25/12/2023", + "Dec 25, 2023","Dec 25 2023","2023-12-25T10:00:00","2023-12-25T10:00:00.123", + "12/25/2023 10:00 AM","12/25/2023 10:00:00 AM","12/25/2023 10:00:00.123 AM","9999-12-31T23:59:59", + "2023-12-25T10:00:00.1","2023-12-25T10:00:00.12", + "2023-12-25T10:00:00.1234","2023-12-25T10:00:00.1234567" }; string[] formats = - { - "MM/dd/yyyy","yyyy-MM-dd","MM-dd-yyyy","dd.MM.yyyy","dd/MM/yyyy", - "MMM dd, yyyy","MMM dd yyyy","yyyy-MM-ddTHH:mm:ss","yyyy-MM-ddTHH:mm:ss.fff", - "yyyy-MM-ddTHH:mm:ss.f","yyyy-MM-ddTHH:mm:ss.ff","yyyy-MM-ddTHH:mm:ss.ffff", - "yyyy-MM-ddTHH:mm:ss.fffffff","MM/dd/yyyy hh:mm tt","MM/dd/yyyy hh:mm:ss tt", - "MM/dd/yyyy hh:mm:ss.fff tt" + { + "MM/dd/yyyy","yyyy-MM-dd","MM-dd-yyyy","dd.MM.yyyy","dd/MM/yyyy", + "MMM dd, yyyy","MMM dd yyyy","yyyy-MM-ddTHH:mm:ss","yyyy-MM-ddTHH:mm:ss.fff", + "yyyy-MM-ddTHH:mm:ss.f","yyyy-MM-ddTHH:mm:ss.ff","yyyy-MM-ddTHH:mm:ss.ffff", + "yyyy-MM-ddTHH:mm:ss.fffffff","MM/dd/yyyy hh:mm tt","MM/dd/yyyy hh:mm:ss tt", + "MM/dd/yyyy hh:mm:ss.fff tt" }; DateTime[] parsedDates = dateStrings .Select(s => DateTime.ParseExact(s, formats, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None)) @@ -211,7 +213,7 @@ public async Task DateTimeArrayRoundtrip_BinaryEncoding_CompareExtraDates_Integr await containerBinaryEncodingEnabled.CreateItemAsync(testItem, new Microsoft.Azure.Cosmos.PartitionKey(pk)); using ResponseMessage response = await containerBinaryEncodingEnabled.ReadItemStreamAsync(testId, new Microsoft.Azure.Cosmos.PartitionKey(pk)); using StreamReader reader = new StreamReader(response.Content, Encoding.UTF8); - rawJsonBEEnabled = await reader.ReadToEndAsync(); + rawJsonBEEnabled = await reader.ReadToEndAsync(); } @@ -232,7 +234,7 @@ public async Task DateTimeArrayRoundtrip_BinaryEncoding_CompareExtraDates_Integr string extraDatesTrue = docTrue.RootElement.GetProperty("ExtraDates").GetRawText(); string extraDatesFalse = docFalse.RootElement.GetProperty("ExtraDates").GetRawText(); - Assert.AreEqual(extraDatesTrue, extraDatesFalse, $"ExtraDates JSON mismatch:\nTrue: {extraDatesTrue}\nFalse: {extraDatesFalse}"); + Assert.AreEqual(extraDatesTrue, extraDatesFalse, $"ExtraDates JSON mismatch:\nTrue: {extraDatesTrue}\nFalse: {extraDatesFalse}"); } finally { @@ -240,1810 +242,2278 @@ public async Task DateTimeArrayRoundtrip_BinaryEncoding_CompareExtraDates_Integr await containerBEDisabledResponse.Container.DeleteContainerAsync(); } } - - [TestMethod] - [TestCategory("MultiRegion")] - [DataRow(FaultInjectionServerErrorType.ServiceUnavailable)] - [DataRow(FaultInjectionServerErrorType.InternalServerError)] - [DataRow(FaultInjectionServerErrorType.DatabaseAccountNotFound)] - [DataRow(FaultInjectionServerErrorType.LeaseNotFound)] - public async Task MetadataEndpointUnavailableCrossRegionalRetryTest(FaultInjectionServerErrorType serverErrorType) - { - FaultInjectionRule collReadBad = new FaultInjectionRuleBuilder( - id: "collread", - condition: new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.MetadataContainer) - .WithRegion(region1) - .Build(), - result: new FaultInjectionServerErrorResultBuilder(serverErrorType) - .Build()) - .Build(); - - FaultInjectionRule pkRangeBad = new FaultInjectionRuleBuilder( - id: "pkrange", - condition: new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.MetadataPartitionKeyRange) - .WithRegion(region1) - .Build(), - result: new FaultInjectionServerErrorResultBuilder(serverErrorType) - .Build()) - .Build(); - - collReadBad.Disable(); - pkRangeBad.Disable(); - - FaultInjector faultInjector = new FaultInjector(new List { pkRangeBad, collReadBad }); - - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConsistencyLevel = ConsistencyLevel.Session, - ConnectionMode = ConnectionMode.Direct, - Serializer = this.cosmosSystemTextJsonSerializer, - FaultInjector = faultInjector, - ApplicationPreferredRegions = new List { region1, region2, region3 } - }; - - using (CosmosClient fiClient = new CosmosClient( - connectionString: this.connectionString, - clientOptions: cosmosClientOptions)) - { - Database fidb = fiClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container fic = fidb.GetContainer(MultiRegionSetupHelpers.containerName); - - pkRangeBad.Enable(); - collReadBad.Enable(); - - try - { - FeedIterator frTest = fic.GetItemQueryIterator("SELECT * FROM c"); - while (frTest.HasMoreResults) - { - FeedResponse feedres = await frTest.ReadNextAsync(); - - Assert.AreEqual(HttpStatusCode.OK, feedres.StatusCode); - } - } - catch (CosmosException ex) - { - Assert.Fail(ex.Message); - } - finally - { - //Cross regional retry needs to ocur (could trigger for other metadata call to try on secondary region so rule would not trigger) - Assert.IsTrue(pkRangeBad.GetHitCount() + collReadBad.GetHitCount() >= 1); - - pkRangeBad.Disable(); - collReadBad.Disable(); - - fiClient.Dispose(); - } - } - } - - [TestMethod] - [TestCategory("MultiRegion")] - public async Task AddressRefreshTimeoutTest() - { - FaultInjectionRule gatewayRule = new FaultInjectionRuleBuilder( - id: "gatewayRule", - condition: new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.MetadataRefreshAddresses) - .WithRegion(region1) - .Build(), - result: new FaultInjectionServerErrorResultBuilder(FaultInjectionServerErrorType.SendDelay) - .WithDelay(TimeSpan.FromSeconds(65)) - .Build()) - .Build(); - - gatewayRule.Disable(); - - FaultInjector faultInjector = new FaultInjector(new List { gatewayRule }); - - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConsistencyLevel = ConsistencyLevel.Session, - ConnectionMode = ConnectionMode.Direct, - Serializer = this.cosmosSystemTextJsonSerializer, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(1), - - }; - - using (CosmosClient fiClient = new CosmosClient( - connectionString: this.connectionString, - clientOptions: cosmosClientOptions)) - { - Database fidb = fiClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container fic = fidb.GetContainer(MultiRegionSetupHelpers.containerName); - - gatewayRule.Enable(); - - try - { - ItemResponse o = await fic.ReadItemAsync( - "testId", - new PartitionKey("pk")); - Assert.IsTrue(o.StatusCode == HttpStatusCode.OK); - } - catch (Exception ex) - { - Assert.Fail(ex.ToString()); - } - finally - { - gatewayRule.Disable(); - Assert.IsTrue(gatewayRule.GetHitCount() >= 3); - - fiClient.Dispose(); - } - } - } - - [Owner("dkunda")] - [TestCategory("MultiRegion")] - [DataRow(true, DisplayName = "Test scenario when binary encoding is enabled at client level.")] - [DataRow(false, DisplayName = "Test scenario when binary encoding is disabled at client level.")] - public async Task ExecuteTransactionalBatch_WhenBinaryEncodingEnabled_ShouldCompleteSuccessfully( - bool isBinaryEncodingEnabled) - { - Environment.SetEnvironmentVariable(ConfigurationManager.BinaryEncodingEnabled, isBinaryEncodingEnabled.ToString()); - - Random random = new(); - CosmosIntegrationTestObject testItem = new() - { - Id = $"smTestId{random.Next()}", - Pk = $"smpk{random.Next()}", - }; - - try - { - CosmosClientOptions cosmosClientOptions = new() - { - ConsistencyLevel = ConsistencyLevel.Session, - RequestTimeout = TimeSpan.FromSeconds(10), - Serializer = new CosmosJsonDotNetSerializer( - cosmosSerializerOptions: new CosmosSerializationOptions() - { - PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase - }, - binaryEncodingEnabled: isBinaryEncodingEnabled) - }; - - using CosmosClient cosmosClient = new( - connectionString: this.connectionString, - clientOptions: cosmosClientOptions); - - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Create a transactional batch - TransactionalBatch transactionalBatch = container.CreateTransactionalBatch(new PartitionKey(testItem.Pk)); - - transactionalBatch.CreateItem( - testItem, - new TransactionalBatchItemRequestOptions - { - EnableContentResponseOnWrite = true, - }); - - transactionalBatch.ReadItem( - testItem.Id, - new TransactionalBatchItemRequestOptions - { - EnableContentResponseOnWrite = true, - }); - - // Execute the transactional batch - TransactionalBatchResponse transactionResponse = await transactionalBatch.ExecuteAsync( - new TransactionalBatchRequestOptions - { - }); - - Assert.AreEqual(HttpStatusCode.OK, transactionResponse.StatusCode); - Assert.AreEqual(2, transactionResponse.Count); - - TransactionalBatchOperationResult createOperationResult = transactionResponse.GetOperationResultAtIndex(0); - - Assert.IsNotNull(createOperationResult); - Assert.IsNotNull(createOperationResult.Resource); - Assert.AreEqual(testItem.Id, createOperationResult.Resource.Id); - Assert.AreEqual(testItem.Pk, createOperationResult.Resource.Pk); - - TransactionalBatchOperationResult readOperationResult = transactionResponse.GetOperationResultAtIndex(1); - - Assert.IsNotNull(readOperationResult); - Assert.IsNotNull(readOperationResult.Resource); - Assert.AreEqual(testItem.Id, readOperationResult.Resource.Id); - Assert.AreEqual(testItem.Pk, readOperationResult.Resource.Pk); - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.BinaryEncodingEnabled, null); - - await this.container.DeleteItemAsync( - testItem.Id, - new PartitionKey(testItem.Pk)); - } - } - - [TestMethod] - [TestCategory("MultiRegion")] - [DataRow(ConnectionMode.Direct, "15", "10", DisplayName = "Direct Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] - [DataRow(ConnectionMode.Direct, "25", "20", DisplayName = "Direct Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] - [DataRow(ConnectionMode.Direct, "35", "30", DisplayName = "Direct Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] - [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Gateway Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] - [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Gateway Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] - [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Gateway Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] - [Owner("dkunda")] - [Timeout(70000)] - public async Task ReadItemAsync_WithCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( - ConnectionMode connectionMode, - string iterationCount, - string circuitBreakerConsecutiveFailureCount) - { - // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); - - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); - - List preferredRegions = new List { region1, region2, region3 }; - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConnectionMode = connectionMode, - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - }; - - List itemsList = new () - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - - int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); - int totalIterations = int.Parse(iterationCount); - - for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) - { - try - { - ItemResponse readResponse = await container.ReadItemAsync( - id: itemsList[0].Id, - partitionKey: new PartitionKey(itemsList[0].Pk)); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: readResponse.StatusCode); - - Assert.IsNotNull(contactedRegions); - - PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( - globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, - isReadOnlyOrMultiMaster: true); - - if (attemptCount > consecutiveFailureCount + 1) - { - if (connectionMode == ConnectionMode.Direct) - { - Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the next region, and the subsequent read request/s were successful on the next region."); - Assert.IsTrue(contactedRegions.Contains(region2)); - } - - Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); - } - else - { - if (connectionMode == ConnectionMode.Direct) - { - Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the read request succeeds before the consecutive failure count reaches the threshold, the partition didn't over to the next region, and the request was retried on the next region."); - Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2)); - } - - if (attemptCount > consecutiveFailureCount) - { - Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); - } - else - { - Assert.AreEqual(this.readRegionsMapping[region1], failoverInfo.Current); - } - } - } - catch (CosmosException) - { - Assert.Fail("Read Item operation should succeed."); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); - - await this.TryDeleteItems(itemsList); - } - } - - [TestMethod] - [TestCategory("MultiRegion")] - [DataRow(ConnectionMode.Direct, DisplayName ="Direct Mode")] - [DataRow(ConnectionMode.Gateway, DisplayName = "Gateway Mode")] - [Owner("nalutripician")] - [Timeout(70000)] - public async Task ReadItemAsync_WithCircuitBreakerEnabledAndTimeoutCounterOverwritten( - ConnectionMode connectionMode) - { - // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerTimeoutCounterResetWindowInMinutes, "0.0833"); // setting to 5 seconds - - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); - - List preferredRegions = new List { region1, region2, region3 }; - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConnectionMode = connectionMode, - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - }; - - List itemsList = new() - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - int readErrorCount = 0; - PartitionKeyRangeFailoverInfo failoverInfo; - - for (int i = 1; i <= 3; i++) - { - try - { - ItemResponse readResponse = await container.ReadItemAsync( - id: itemsList[0].Id, - partitionKey: new PartitionKey(itemsList[0].Pk)); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: readResponse.StatusCode); - - Assert.IsNotNull(contactedRegions); - - failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( - globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, - isReadOnlyOrMultiMaster: true); + [TestMethod] + [TestCategory("MultiRegion")] + [DataRow(FaultInjectionServerErrorType.ServiceUnavailable)] + [DataRow(FaultInjectionServerErrorType.InternalServerError)] + [DataRow(FaultInjectionServerErrorType.DatabaseAccountNotFound)] + [DataRow(FaultInjectionServerErrorType.LeaseNotFound)] + public async Task MetadataEndpointUnavailableCrossRegionalRetryTest(FaultInjectionServerErrorType serverErrorType) + { + FaultInjectionRule collReadBad = new FaultInjectionRuleBuilder( + id: "collread", + condition: new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.MetadataContainer) + .WithRegion(region1) + .Build(), + result: new FaultInjectionServerErrorResultBuilder(serverErrorType) + .Build()) + .Build(); - failoverInfo.SnapshotConsecutiveRequestFailureCount(out readErrorCount, out _); - - Assert.IsTrue(readErrorCount > 0); - } - catch (CosmosException) - { - Assert.Fail("Read Item operation should succeed."); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); - } - } + FaultInjectionRule pkRangeBad = new FaultInjectionRuleBuilder( + id: "pkrange", + condition: new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.MetadataPartitionKeyRange) + .WithRegion(region1) + .Build(), + result: new FaultInjectionServerErrorResultBuilder(serverErrorType) + .Build()) + .Build(); - await Task.Delay(6000); // Wait for the timeout counter to reset + collReadBad.Disable(); + pkRangeBad.Disable(); - try - { - ItemResponse readResponse = await container.ReadItemAsync( - id: itemsList[0].Id, - partitionKey: new PartitionKey(itemsList[0].Pk)); - } - catch (CosmosException) - { - Assert.Fail("Read Item operation should succeed after the timeout counter is overwritten."); - } + FaultInjector faultInjector = new FaultInjector(new List { pkRangeBad, collReadBad }); - failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( - globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, - isReadOnlyOrMultiMaster: true); + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConsistencyLevel = ConsistencyLevel.Session, + ConnectionMode = ConnectionMode.Direct, + Serializer = this.cosmosSystemTextJsonSerializer, + FaultInjector = faultInjector, + ApplicationPreferredRegions = new List { region1, region2, region3 } + }; - failoverInfo.SnapshotConsecutiveRequestFailureCount(out int currentReadErrorCount, out _); + using (CosmosClient fiClient = new CosmosClient( + connectionString: this.connectionString, + clientOptions: cosmosClientOptions)) + { + Database fidb = fiClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container fic = fidb.GetContainer(MultiRegionSetupHelpers.containerName); - Assert.AreEqual(1, currentReadErrorCount, "The read error count should be reset after the timeout counter is overwritten. Then after one more failure it should be incremented by 1."); - Assert.IsTrue(readErrorCount > currentReadErrorCount, "The read error count should be greater than the current before the timeout counter is overwritten."); - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); - - await this.TryDeleteItems(itemsList); - } - } - - [TestMethod] - [TestCategory("MultiRegion")] - [Owner("dkunda")] - [Timeout(70000)] - public async Task ReadItemAsync_WithCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceivedFromTwoRegions_ShouldApplyPartitionLevelOverrideToThridRegion() - { - // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, "10"); - - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId1 = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule1 = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId1, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - string serviceUnavailableRuleId2 = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule2 = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId2, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(region2) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - serviceUnavailableRule1.Disable(); - serviceUnavailableRule2.Disable(); - - List rules = new List { serviceUnavailableRule1, serviceUnavailableRule2 }; - FaultInjector faultInjector = new FaultInjector(rules); - - List preferredRegions = new List { region1, region2, region3 }; - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - }; - - List itemsList = new() - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new (connectionString: this.connectionString, clientOptions: cosmosClientOptions); - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - - bool isRegion1Available = true; - bool isRegion2Available = true; - - int thresholdCounter = 0; - int totalIterations = 40; - int ppcbDefaultThreshold = 10; - int firstRegionServiceUnavailableAttempt = 3; - int secondRegionServiceUnavailableAttempt = 28; - - for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) - { - try - { - ItemResponse readResponse = await container.ReadItemAsync( - id: itemsList[0].Id, - partitionKey: new PartitionKey(itemsList[0].Pk)); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: readResponse.StatusCode); - - Assert.IsNotNull(contactedRegions); - - if (isRegion1Available && isRegion2Available) - { - Assert.IsTrue(contactedRegions.Count == 1, "Assert that, when no failure happened, the read request is being served from region 1."); - Assert.IsTrue(contactedRegions.Contains(region1)); - - // Simulating service unavailable on region 1. - if (attemptCount == firstRegionServiceUnavailableAttempt) - { - isRegion1Available = false; - serviceUnavailableRule1.Enable(); - } - } - else if (isRegion2Available) - { - if (thresholdCounter <= ppcbDefaultThreshold) - { - Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the read request succeeds before the consecutive failure count reaches the threshold, the partition didn't fail over to the next region, and the request was retried."); - Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2)); - thresholdCounter++; - } - else - { - Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the next region, and the subsequent read request/s were successful on the next region."); - Assert.IsTrue(contactedRegions.Contains(region2)); - } - - // Simulating service unavailable on region 2. - if (attemptCount == secondRegionServiceUnavailableAttempt) - { - isRegion2Available = false; - serviceUnavailableRule2.Enable(); - } - } - else - { - if (thresholdCounter <= ppcbDefaultThreshold + 1) - { - Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the read request fails on the second region, the partition did over to the next region, and the request was retried on the next region."); - Assert.IsTrue(contactedRegions.Contains(region2) && contactedRegions.Contains(region3)); - thresholdCounter++; - } - else - { - Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the third region, and the subsequent read request/s were successful on the third region."); - Assert.IsTrue(contactedRegions.Contains(region3)); - } - } - } - catch (CosmosException) - { - Assert.Fail("Read Item operation should succeed."); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); - - await this.TryDeleteItems(itemsList); - } - } - - [TestMethod] - [TestCategory("MultiRegion")] - [Owner("dkunda")] - [Timeout(70000)] - public async Task ReadItemAsync_WithNoPreferredRegionsAndCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride() - { - // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, "10"); - - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); - - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConnectionMode = ConnectionMode.Direct, - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - }; - - List itemsList = new() - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - - int consecutiveFailureCount = 10; - int totalIterations = 15; - - for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) - { - try - { - ItemResponse readResponse = await container.ReadItemAsync( - id: itemsList[0].Id, - partitionKey: new PartitionKey(itemsList[0].Pk)); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: readResponse.StatusCode); - - Assert.IsNotNull(contactedRegions); - - PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( - globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, - isReadOnlyOrMultiMaster: true); - - if (attemptCount > consecutiveFailureCount + 1) - { - Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the next region, and the subsequent read request/s were successful on the next region."); - Assert.IsTrue(contactedRegions.Contains(region2)); - Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); - } - else - { - Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the read request succeeds before the consecutive failure count reaches the threshold, the partition didn't over to the next region, and the request was retried on the next region."); - Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2)); - - if (attemptCount > consecutiveFailureCount) - { - Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); - } - else - { - Assert.AreEqual(this.readRegionsMapping[region1], failoverInfo.Current); - } - } - } - catch (CosmosException) - { - Assert.Fail("Read Item operation should succeed."); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); - - await this.TryDeleteItems(itemsList); - } - } - - [TestMethod] - [Owner("dkunda")] - [TestCategory("MultiRegion")] - [Timeout(70000)] - public async Task ReadItemAsync_WithCircuitBreakerDisabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldNotApplyPartitionLevelOverride() - { - // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "False"); - - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); - - List preferredRegions = new List { region1, region2, region3 }; - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - }; - - List itemsList = new() - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - - int consecutiveFailureCount = 10; - for (int attemptCount = 1; attemptCount <= consecutiveFailureCount; attemptCount++) - { - try - { - ItemResponse readResponse = await container.ReadItemAsync( - id: itemsList[0].Id, - partitionKey: new PartitionKey(itemsList[0].Pk)); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: readResponse.StatusCode); - - Assert.IsNotNull(contactedRegions); - Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the read request succeeds after failover, the partition was failed over to the next region, after the failures reaches the threshold."); - Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2)); - } - catch (CosmosException) - { - Assert.Fail("Read Item operation should succeed."); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); - - await this.TryDeleteItems(itemsList); - } - } - - [TestMethod] - [Owner("dkunda")] - [TestCategory("MultiRegion")] - [Timeout(70000)] - public async Task CreateItemAsync_WithCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldNotApplyPartitionLevelOverride() - { - // Arrange. - int circuitBreakerConsecutiveFailureCount = 10; - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, $"{circuitBreakerConsecutiveFailureCount}"); - - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.CreateItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); - - List preferredRegions = new List { region1, region2, region3 }; - CosmosClientOptions cosmosClientOptions = new() - { - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - }; - - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - try - { - // Act and Assert. - for (int attemptCount = 1; attemptCount <= circuitBreakerConsecutiveFailureCount; attemptCount++) - { - try - { - CosmosIntegrationTestObject testItem = new() { Id = "testId5", Pk = "pk5" }; - ItemResponse createResponse = await container.CreateItemAsync(testItem); - Assert.Fail("Create Item operation should not succeed."); - } - catch (CosmosException ex) - { - Assert.AreEqual( - expected: HttpStatusCode.ServiceUnavailable, - actual: ex.StatusCode); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = ex.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when a 503 Service Unavailable happens, the partition was not failed over to the next region, since writes are not supported in a single master account, when circuit breaker is enabled."); - Assert.IsTrue(contactedRegions.Contains(region1)); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); - } - } - - [TestMethod] - [Owner("dkunda")] - [TestCategory("MultiMaster")] - [DataRow(ConnectionMode.Direct, "15", "10", DisplayName = "Direct Mode - Scenario whtn the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] - [DataRow(ConnectionMode.Direct, "25", "20", DisplayName = "Direct Mode - Scenario whtn the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] - [DataRow(ConnectionMode.Direct, "35", "30", DisplayName = "Direct Mode - Scenario whtn the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] - [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Gateway Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] - [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Gateway Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] - [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Gateway Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] - [Timeout(70000)] - public async Task CreateItemAsync_WithCircuitBreakerEnabledAndMultiMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( - ConnectionMode connectionMode, - string iterationCount, - string circuitBreakerConsecutiveFailureCount) - { - // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForWrites, circuitBreakerConsecutiveFailureCount); - - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.CreateItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); - - Random random = new (); - List itemsCleanupList = new(); - List preferredRegions = new List { region1, region2, region3 }; - CosmosClientOptions cosmosClientOptions = new() - { - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - Serializer = this.cosmosSystemTextJsonSerializer, - ConnectionMode = connectionMode, - }; - - try - { - // Act and Assert. - int totalIterations = int.Parse(iterationCount); - int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); - - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) - { - try - { - CosmosIntegrationTestObject testItem = new() - { - Id = $"mmTestId{random.Next()}", - Pk = $"mmpk{random.Next()}" - }; - - ItemResponse createResponse = await container.CreateItemAsync(testItem); - itemsCleanupList.Add(testItem); - - Assert.AreEqual( - expected: HttpStatusCode.Created, - actual: createResponse.StatusCode); - - PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( - globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, - isReadOnlyOrMultiMaster: true); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = createResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - Assert.IsNotNull(contactedRegions); - - if (attemptCount > consecutiveFailureCount + 1) - { - if (connectionMode == ConnectionMode.Direct) - { - Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the next region, and the subsequent write request/s were successful on the next region."); - Assert.IsTrue(contactedRegions.Contains(region2)); - } - - Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); - } - else - { - if (connectionMode == ConnectionMode.Direct) - { - Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the write requests succeeds before the consecutive failure count reaches the threshold, the partition didn't over to the next region, and the request was retried on the next region."); - Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2)); - } - if (attemptCount > consecutiveFailureCount) - { - Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); - } - else - { - Assert.AreEqual(this.readRegionsMapping[region1], failoverInfo.Current); - } - } - } - catch (CosmosException) - { - Assert.Fail("Create Item operation should succeed."); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during CreateItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForWrites, null); - - foreach (CosmosIntegrationTestObject item in itemsCleanupList) - { - await this.container.DeleteItemAsync(item.Id, new PartitionKey(item.Pk)); - } - } - } - - [TestMethod] - [Owner("dkunda")] - [TestCategory("MultiMaster")] - [Timeout(70000)] - public async Task CreateAndReadItemAsync_WithCircuitBreakerEnabledAndMultiMasterAccountAndDefaultThresholdServiceUnavailableReceived_ShouldApplyPartitionLevelOverride() - { - // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId1 = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule1 = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId1, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.CreateItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(2)) - .Build()) - .Build(); - - string serviceUnavailableRuleId2 = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule2 = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId2, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(2)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule1, serviceUnavailableRule2 }; - FaultInjector faultInjector = new FaultInjector(rules); - - List itemsCleanupList = new(); - List preferredRegions = new List { region1, region2, region3 }; - CosmosClientOptions cosmosClientOptions = new() - { - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - Serializer = this.cosmosSystemTextJsonSerializer - }; - - try - { - // Act and Assert. - Random random = new (); - int totalIterations = 20; - int consecutiveFailureCountForReads = 10; - int consecutiveFailureCountForWrites = 5; - - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) - { - try - { - CosmosIntegrationTestObject testItem = new() - { - Id = $"mmTestId{random.Next()}", - Pk = $"mmpk{random.Next()}" - }; - - ItemResponse createResponse = await container.CreateItemAsync(testItem); - itemsCleanupList.Add(testItem); - - Assert.AreEqual( - expected: HttpStatusCode.Created, - actual: createResponse.StatusCode); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMappingForWrites = createResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegionsForWrite = new(contactedRegionMappingForWrites.Select(r => r.regionName)); - Assert.IsNotNull(contactedRegionsForWrite); - - if (attemptCount > consecutiveFailureCountForWrites + 1) - { - Assert.IsTrue(contactedRegionsForWrite.Count == 1, "Asserting that when the consecutive failure count reaches the write threshold, the partition was failed over to the next region, and the subsequent write request/s were successful on the next region."); - Assert.IsTrue(contactedRegionsForWrite.Contains(region2)); - } - else - { - Assert.IsTrue(contactedRegionsForWrite.Count == 2, "Asserting that when the write requests succeeds before the consecutive failure count reaches the write threshold, the partition didn't over to the next region, and the request was retried on the next region."); - Assert.IsTrue(contactedRegionsForWrite.Contains(region1) && contactedRegionsForWrite.Contains(region2)); - } - - ItemResponse readResponse = await container.ReadItemAsync( - id: testItem.Id, - partitionKey: new PartitionKey(testItem.Pk)); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMappingForReads = readResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegionsForReads = new(contactedRegionMappingForReads.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: readResponse.StatusCode); - - Assert.IsNotNull(contactedRegionsForReads); - - if (attemptCount > consecutiveFailureCountForReads + 1) - { - Assert.IsTrue(contactedRegionsForReads.Count == 1, "Asserting that when the consecutive failure count reaches the read threshold, the partition was failed over to the next region, and the subsequent read request/s were successful on the next region."); - Assert.IsTrue(contactedRegionsForReads.Contains(region2)); - } - else - { - Assert.IsTrue(contactedRegionsForReads.Count == 2, "Asserting that when the read request succeeds before the consecutive failure count reaches the read threshold, the partition didn't over to the next region, and the request was retried on the next region."); - Assert.IsTrue(contactedRegionsForReads.Contains(region1) && contactedRegionsForReads.Contains(region2)); - } - } - catch (CosmosException ex) - { - Assert.Fail($"Create and Read Item operations should succeed. Message: { ex.Message}"); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during CreateItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - - foreach (CosmosIntegrationTestObject item in itemsCleanupList) - { - await this.container.DeleteItemAsync(item.Id, new PartitionKey(item.Pk)); - } - } - } - - [TestMethod] - [Owner("dkunda")] - [TestCategory("MultiRegion")] - [Timeout(70000)] - [DataRow(true, DisplayName = "Test scenario when PPAF is enabled at client level.")] - [DataRow(false, DisplayName = "Test scenario when PPAF is disabled at client level.")] - public async Task ReadItemAsync_WithPPAFEnabledAndSingleMasterAccountWithResponseDelay_ShouldHedgeRequestToMultipleRegions( - bool enablePartitionLevelFailover) - { - // Arrange. - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ResponseDelay) - .WithDelay(TimeSpan.FromMilliseconds(3000)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); + pkRangeBad.Enable(); + collReadBad.Enable(); - // Now that the ppaf enablement flag is returned from gateway, we need to intercept the response and remove the flag from the response, so that - // the environment variable set above is honored. - HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper() - { - ResponseIntercepter = async (response, request) => + try { - string json = await response?.Content?.ReadAsStringAsync(); - if (json.Length > 0 && json.Contains("enablePerPartitionFailoverBehavior")) + FeedIterator frTest = fic.GetItemQueryIterator("SELECT * FROM c"); + while (frTest.HasMoreResults) { - JObject parsedDatabaseAccountResponse = JObject.Parse(json); - parsedDatabaseAccountResponse.Property("enablePerPartitionFailoverBehavior").Value = enablePartitionLevelFailover.ToString(); - - HttpResponseMessage interceptedResponse = new() - { - StatusCode = response.StatusCode, - Content = new StringContent(parsedDatabaseAccountResponse.ToString()), - Version = response.Version, - ReasonPhrase = response.ReasonPhrase, - RequestMessage = response.RequestMessage, - }; + FeedResponse feedres = await frTest.ReadNextAsync(); - return interceptedResponse; + Assert.AreEqual(HttpStatusCode.OK, feedres.StatusCode); } - - return response; - }, - }; - - List preferredRegions = new List { region1, region2, region3 }; - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - HttpClientFactory = () => new HttpClient(httpClientHandlerHelper), - }; - - List itemsList = new() - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - - ItemResponse readResponse = await container.ReadItemAsync( - id: itemsList[0].Id, - partitionKey: new PartitionKey(itemsList[0].Pk)); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: readResponse.StatusCode); - - CosmosTraceDiagnostics traceDiagnostic = readResponse.Diagnostics as CosmosTraceDiagnostics; - Assert.IsNotNull(traceDiagnostic); - - traceDiagnostic.Value.Data.TryGetValue("Hedge Context", out object hedgeContext); - - if (enablePartitionLevelFailover) - { - Assert.IsNotNull(hedgeContext); - List hedgedRegions = ((IEnumerable)hedgeContext).ToList(); - - Assert.IsTrue(hedgedRegions.Count > 1, "Since the first region is not available, the request should atleast hedge to the next region."); - Assert.IsTrue(hedgedRegions.Contains(region1) && (hedgedRegions.Contains(region2) || hedgedRegions.Contains(region3))); - } - else - { - Assert.IsNull(hedgeContext); - } - - Assert.IsNotNull(contactedRegions); - Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the read request succeeds on any region, given that there were no availability loss."); - } - finally - { - await this.TryDeleteItems(itemsList); - } - } - - [TestMethod] - [Owner("nalutripician")] - [TestCategory("MultiRegion")] - [Timeout(70000)] - [DataRow(true, DisplayName = "Test scenario when PPAF is enabled at client level.")] - [DataRow(false, DisplayName = "Test scenario when PPAF is disabled at client level.")] - public async Task ReadItemAsync_WithPPAFDiableOverride( - bool enablePartitionLevelFailover) - { - // Arrange. - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(region1) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ResponseDelay) - .WithDelay(TimeSpan.FromMilliseconds(3000)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); - - // Now that the ppaf enablement flag is returned from gateway, we need to intercept the response and remove the flag from the response, so that - // the environment variable set above is honored. - HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper() - { - ResponseIntercepter = async (response, request) => + } + catch (CosmosException ex) { - string json = await response?.Content?.ReadAsStringAsync(); - if (json.Length > 0 && json.Contains("enablePerPartitionFailoverBehavior")) - { - JObject parsedDatabaseAccountResponse = JObject.Parse(json); - parsedDatabaseAccountResponse.Property("enablePerPartitionFailoverBehavior").Value = enablePartitionLevelFailover.ToString(); - - HttpResponseMessage interceptedResponse = new() - { - StatusCode = response.StatusCode, - Content = new StringContent(parsedDatabaseAccountResponse.ToString()), - Version = response.Version, - ReasonPhrase = response.ReasonPhrase, - RequestMessage = response.RequestMessage, - }; + Assert.Fail(ex.Message); + } + finally + { + //Cross regional retry needs to ocur (could trigger for other metadata call to try on secondary region so rule would not trigger) + Assert.IsTrue(pkRangeBad.GetHitCount() + collReadBad.GetHitCount() >= 1); - return interceptedResponse; - } + pkRangeBad.Disable(); + collReadBad.Disable(); - return response; - }, - }; - - List preferredRegions = new List { region1, region2, region3 }; - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - HttpClientFactory = () => new HttpClient(httpClientHandlerHelper), - DisablePartitionLevelFailover = true, // This will disable the PPAF override for this test. - }; - - List itemsList = new() - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - - ItemResponse readResponse = await container.ReadItemAsync( - id: itemsList[0].Id, - partitionKey: new PartitionKey(itemsList[0].Pk)); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: readResponse.StatusCode); - - CosmosTraceDiagnostics traceDiagnostic = readResponse.Diagnostics as CosmosTraceDiagnostics; - Assert.IsNotNull(traceDiagnostic); - - traceDiagnostic.Value.Data.TryGetValue("Hedge Context", out object hedgeContext); - - Assert.IsNull(hedgeContext); - - Assert.IsNotNull(contactedRegions); - Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the read request succeeds on any region, given that there were no availability loss."); - } - finally - { - await this.TryDeleteItems(itemsList); - } + fiClient.Dispose(); + } + } } [TestMethod] [TestCategory("MultiRegion")] - [Owner("ntripician")] - public async Task AddressRefreshInternalServerErrorTest() + public async Task AddressRefreshTimeoutTest() { - FaultInjectionRule internalServerError = new FaultInjectionRuleBuilder( - id: "rule1", + FaultInjectionRule gatewayRule = new FaultInjectionRuleBuilder( + id: "gatewayRule", condition: new FaultInjectionConditionBuilder() .WithOperationType(FaultInjectionOperationType.MetadataRefreshAddresses) .WithRegion(region1) .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.InternalServerError) + result: new FaultInjectionServerErrorResultBuilder(FaultInjectionServerErrorType.SendDelay) + .WithDelay(TimeSpan.FromSeconds(65)) .Build()) .Build(); - List rules = new List() { internalServerError }; - FaultInjector faultInjector = new FaultInjector(rules); + gatewayRule.Disable(); - internalServerError.Disable(); + FaultInjector faultInjector = new FaultInjector(new List { gatewayRule }); - CosmosClientOptions clientOptions = new CosmosClientOptions() + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() { + ConsistencyLevel = ConsistencyLevel.Session, ConnectionMode = ConnectionMode.Direct, Serializer = this.cosmosSystemTextJsonSerializer, - ApplicationRegion = region1, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(1), + }; - using (CosmosClient faultInjectionClient = new CosmosClient( + using (CosmosClient fiClient = new CosmosClient( connectionString: this.connectionString, - clientOptions: faultInjector.GetFaultInjectionClientOptions(clientOptions))) + clientOptions: cosmosClientOptions)) { - Database database = faultInjectionClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + Database fidb = fiClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container fic = fidb.GetContainer(MultiRegionSetupHelpers.containerName); - internalServerError.Enable(); + gatewayRule.Enable(); try { - ItemResponse response = await container.ReadItemAsync("testId", new PartitionKey("pk")); - Assert.IsTrue(internalServerError.GetHitCount() > 0); - Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + ItemResponse o = await fic.ReadItemAsync( + "testId", + new PartitionKey("pk")); + Assert.IsTrue(o.StatusCode == HttpStatusCode.OK); } - catch (CosmosException ex) + catch (Exception ex) { - Assert.Fail(ex.Message); + Assert.Fail(ex.ToString()); } - } - } + finally + { + gatewayRule.Disable(); + Assert.IsTrue(gatewayRule.GetHitCount() >= 3); - [TestMethod] - [TestCategory("MultiRegion")] - [Ignore("We will enable this test once the test staging account used for multi master validation starts supporting thin proxy.")] - [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] - [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] - [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] - [Owner("dkunda")] - [Timeout(70000)] - public async Task ReadItemAsync_WithThinClientCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( - ConnectionMode connectionMode, - string iterationCount, - string circuitBreakerConsecutiveFailureCount) + fiClient.Dispose(); + } + } + } + + [Owner("dkunda")] + [TestCategory("MultiRegion")] + [DataRow(true, DisplayName = "Test scenario when binary encoding is enabled at client level.")] + [DataRow(false, DisplayName = "Test scenario when binary encoding is disabled at client level.")] + public async Task ExecuteTransactionalBatch_WhenBinaryEncodingEnabled_ShouldCompleteSuccessfully( + bool isBinaryEncodingEnabled) { - // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); + Environment.SetEnvironmentVariable(ConfigurationManager.BinaryEncodingEnabled, isBinaryEncodingEnabled.ToString()); - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(Regions.WestUS) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); - - List preferredRegions = new List { Regions.WestUS, Regions.EastAsia }; - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConnectionMode = connectionMode, - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - }; - - List itemsList = new() - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - AccountProperties accountInfo = await cosmosClient.ReadAccountAsync(); + Random random = new(); + CosmosIntegrationTestObject testItem = new() + { + Id = $"smTestId{random.Next()}", + Pk = $"smpk{random.Next()}", + }; - Assert.IsTrue(cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints.Count() >= 2); - this.thinClientreadRegionalEndpoints = cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints; - - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - - int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); - int totalIterations = int.Parse(iterationCount); - - for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) - { - try - { - ItemResponse readResponse = await container.ReadItemAsync( - id: itemsList[0].Id, - partitionKey: new PartitionKey(itemsList[0].Pk)); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: readResponse.StatusCode); - - Assert.IsNotNull(contactedRegions); - - PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( - globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, - isReadOnlyOrMultiMaster: true); - - if (attemptCount > consecutiveFailureCount) - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); - } - else - { - if (attemptCount == consecutiveFailureCount) - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); - } - else - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[0], failoverInfo.Current); - } - } - } - catch (CosmosException ce) - { - Assert.Fail("Read Item operation should succeed." + ce); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); - Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, null); - - await this.TryDeleteItems(itemsList); - } + try + { + CosmosClientOptions cosmosClientOptions = new() + { + ConsistencyLevel = ConsistencyLevel.Session, + RequestTimeout = TimeSpan.FromSeconds(10), + Serializer = new CosmosJsonDotNetSerializer( + cosmosSerializerOptions: new CosmosSerializationOptions() + { + PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase + }, + binaryEncodingEnabled: isBinaryEncodingEnabled) + }; + + using CosmosClient cosmosClient = new( + connectionString: this.connectionString, + clientOptions: cosmosClientOptions); + + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Create a transactional batch + TransactionalBatch transactionalBatch = container.CreateTransactionalBatch(new PartitionKey(testItem.Pk)); + + transactionalBatch.CreateItem( + testItem, + new TransactionalBatchItemRequestOptions + { + EnableContentResponseOnWrite = true, + }); + + transactionalBatch.ReadItem( + testItem.Id, + new TransactionalBatchItemRequestOptions + { + EnableContentResponseOnWrite = true, + }); + + // Execute the transactional batch + TransactionalBatchResponse transactionResponse = await transactionalBatch.ExecuteAsync( + new TransactionalBatchRequestOptions + { + }); + + Assert.AreEqual(HttpStatusCode.OK, transactionResponse.StatusCode); + Assert.AreEqual(2, transactionResponse.Count); + + TransactionalBatchOperationResult createOperationResult = transactionResponse.GetOperationResultAtIndex(0); + + Assert.IsNotNull(createOperationResult); + Assert.IsNotNull(createOperationResult.Resource); + Assert.AreEqual(testItem.Id, createOperationResult.Resource.Id); + Assert.AreEqual(testItem.Pk, createOperationResult.Resource.Pk); + + TransactionalBatchOperationResult readOperationResult = transactionResponse.GetOperationResultAtIndex(1); + + Assert.IsNotNull(readOperationResult); + Assert.IsNotNull(readOperationResult.Resource); + Assert.AreEqual(testItem.Id, readOperationResult.Resource.Id); + Assert.AreEqual(testItem.Pk, readOperationResult.Resource.Pk); + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.BinaryEncodingEnabled, null); + + await this.container.DeleteItemAsync( + testItem.Id, + new PartitionKey(testItem.Pk)); + } } - [TestMethod] - [TestCategory("MultiMaster")] - [Ignore ("We will enable this test once the test staging account used for multi master validation starts supporting thin proxy.")] - [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] - [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] - [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] - [Owner("dkunda")] - [Timeout(70000)] - public async Task CreateItemAsync_WithThinClientEnabledAndCircuitBreakerEnabledAndMultiMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( - ConnectionMode connectionMode, - string iterationCount, - string circuitBreakerConsecutiveFailureCount) + [TestMethod] + [TestCategory("MultiRegion")] + [DataRow(ConnectionMode.Direct, "15", "10", DisplayName = "Direct Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] + [DataRow(ConnectionMode.Direct, "25", "20", DisplayName = "Direct Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] + [DataRow(ConnectionMode.Direct, "35", "30", DisplayName = "Direct Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] + [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Gateway Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] + [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Gateway Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] + [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Gateway Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] + [Owner("dkunda")] + [Timeout(70000)] + public async Task ReadItemAsync_WithCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( + ConnectionMode connectionMode, + string iterationCount, + string circuitBreakerConsecutiveFailureCount) { // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.CreateItem) - .WithRegion(Regions.WestUS) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; FaultInjector faultInjector = new FaultInjector(rules); - Random random = new(); - List itemsCleanupList = new(); - List preferredRegions = new List { Regions.WestUS, Regions.EastAsia }; - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConnectionMode = connectionMode, - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - }; - - List itemsList = new() - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - AccountProperties accountInfo = await cosmosClient.ReadAccountAsync(); + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConnectionMode = connectionMode, + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + }; - Assert.IsTrue(cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints.Count() >= 2); - this.thinClientreadRegionalEndpoints = cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints; - - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - - int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); - int totalIterations = int.Parse(iterationCount); - - for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) - { - try - { - CosmosIntegrationTestObject testItem = new() - { - Id = $"mmTestId{random.Next()}", - Pk = $"mmpk{random.Next()}" - }; - - ItemResponse createResponse = await container.CreateItemAsync(testItem); - itemsCleanupList.Add(testItem); - - Assert.AreEqual( - expected: HttpStatusCode.Created, - actual: createResponse.StatusCode); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = createResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: createResponse.StatusCode); - - Assert.IsNotNull(contactedRegions); - - PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( - globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, - isReadOnlyOrMultiMaster: true); - - if (attemptCount > consecutiveFailureCount) - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); - } - else - { - if (attemptCount == consecutiveFailureCount) - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); - } - else - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[0], failoverInfo.Current); - } - } - } - catch (CosmosException ce) - { - Assert.Fail("Create Item operation should succeed." + ce); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during CreateItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); - Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, null); - - await this.TryDeleteItems(itemsList); - } + List itemsList = new () + { + new() { Id = Guid.NewGuid().ToString(), Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); + int totalIterations = int.Parse(iterationCount); + + for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) + { + try + { + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + Assert.IsNotNull(contactedRegions); + + PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( + globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, + isReadOnlyOrMultiMaster: true); + + if (attemptCount > consecutiveFailureCount + 1) + { + if (connectionMode == ConnectionMode.Direct) + { + Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the next region, and the subsequent read request/s were successful on the next region."); + Assert.IsTrue(contactedRegions.Contains(region2)); + } + + Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); + } + else + { + if (connectionMode == ConnectionMode.Direct) + { + Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the read request succeeds before the consecutive failure count reaches the threshold, the partition didn't over to the next region, and the request was retried on the next region."); + Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2)); + } + + if (attemptCount > consecutiveFailureCount) + { + Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); + } + else + { + Assert.AreEqual(this.readRegionsMapping[region1], failoverInfo.Current); + } + } + } + catch (CosmosException) + { + Assert.Fail("Read Item operation should succeed."); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + + await this.TryDeleteItems(itemsList); + } + } + + [TestMethod] + [TestCategory("MultiRegion")] + [DataRow(ConnectionMode.Direct, DisplayName ="Direct Mode")] + [DataRow(ConnectionMode.Gateway, DisplayName = "Gateway Mode")] + [Owner("nalutripician")] + [Timeout(70000)] + public async Task ReadItemAsync_WithCircuitBreakerEnabledAndTimeoutCounterOverwritten( + ConnectionMode connectionMode) + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerTimeoutCounterResetWindowInMinutes, "0.0833"); // setting to 5 seconds + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConnectionMode = connectionMode, + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + }; + + List itemsList = new() + { + new() { Id = Guid.NewGuid().ToString(), Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + int readErrorCount = 0; + PartitionKeyRangeFailoverInfo failoverInfo; + + for (int i = 1; i <= 3; i++) + { + try + { + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + Assert.IsNotNull(contactedRegions); + + failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( + globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, + isReadOnlyOrMultiMaster: true); + + failoverInfo.SnapshotConsecutiveRequestFailureCount(out readErrorCount, out _); + + Assert.IsTrue(readErrorCount > 0); + } + catch (CosmosException) + { + Assert.Fail("Read Item operation should succeed."); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); + } + } + + await Task.Delay(6000); // Wait for the timeout counter to reset + + try + { + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + } + catch (CosmosException) + { + Assert.Fail("Read Item operation should succeed after the timeout counter is overwritten."); + } + + failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( + globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, + isReadOnlyOrMultiMaster: true); + + failoverInfo.SnapshotConsecutiveRequestFailureCount(out int currentReadErrorCount, out _); + + Assert.AreEqual(1, currentReadErrorCount, "The read error count should be reset after the timeout counter is overwritten. Then after one more failure it should be incremented by 1."); + Assert.IsTrue(readErrorCount > currentReadErrorCount, "The read error count should be greater than the current before the timeout counter is overwritten."); + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerTimeoutCounterResetWindowInMinutes, null); + await this.TryDeleteItems(itemsList); + } + } + + [TestMethod] + [TestCategory("MultiRegion")] + [Owner("dkunda")] + [Timeout(70000)] + public async Task ReadItemAsync_WithCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceivedFromTwoRegions_ShouldApplyPartitionLevelOverrideToThridRegion() + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, "10"); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId1 = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule1 = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId1, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + string serviceUnavailableRuleId2 = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule2 = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId2, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(region2) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + serviceUnavailableRule1.Disable(); + serviceUnavailableRule2.Disable(); + + List rules = new List { serviceUnavailableRule1, serviceUnavailableRule2 }; + FaultInjector faultInjector = new FaultInjector(rules); + + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + }; + + List itemsList = new() + { + new() { Id = "smTestId1", Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new (connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + bool isRegion1Available = true; + bool isRegion2Available = true; + + int thresholdCounter = 0; + int totalIterations = 40; + int ppcbDefaultThreshold = 10; + int firstRegionServiceUnavailableAttempt = 3; + int secondRegionServiceUnavailableAttempt = 28; + + for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) + { + try + { + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + Assert.IsNotNull(contactedRegions); + + if (isRegion1Available && isRegion2Available) + { + Assert.IsTrue(contactedRegions.Count == 1, "Assert that, when no failure happened, the read request is being served from region 1."); + Assert.IsTrue(contactedRegions.Contains(region1)); + + // Simulating service unavailable on region 1. + if (attemptCount == firstRegionServiceUnavailableAttempt) + { + isRegion1Available = false; + serviceUnavailableRule1.Enable(); + } + } + else if (isRegion2Available) + { + if (thresholdCounter <= ppcbDefaultThreshold) + { + Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the read request succeeds before the consecutive failure count reaches the threshold, the partition didn't fail over to the next region, and the request was retried."); + Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2)); + thresholdCounter++; + } + else + { + Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the next region, and the subsequent read request/s were successful on the next region."); + Assert.IsTrue(contactedRegions.Contains(region2)); + } + + // Simulating service unavailable on region 2. + if (attemptCount == secondRegionServiceUnavailableAttempt) + { + isRegion2Available = false; + serviceUnavailableRule2.Enable(); + } + } + else + { + if (thresholdCounter <= ppcbDefaultThreshold + 1) + { + Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the read request fails on the second region, the partition did over to the next region, and the request was retried on the next region."); + Assert.IsTrue(contactedRegions.Contains(region2) && contactedRegions.Contains(region3)); + thresholdCounter++; + } + else + { + Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the third region, and the subsequent read request/s were successful on the third region."); + Assert.IsTrue(contactedRegions.Contains(region3)); + } + } + } + catch (CosmosException) + { + Assert.Fail("Read Item operation should succeed."); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + + await this.TryDeleteItems(itemsList); + } + } + + [TestMethod] + [TestCategory("MultiRegion")] + [Owner("dkunda")] + [Timeout(70000)] + public async Task ReadItemAsync_WithNoPreferredRegionsAndCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride() + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, "10"); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Direct, + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + }; + + List itemsList = new() + { + new() { Id = Guid.NewGuid().ToString(), Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + int consecutiveFailureCount = 10; + int totalIterations = 15; + + for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) + { + try + { + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + Assert.IsNotNull(contactedRegions); + + PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( + globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, + isReadOnlyOrMultiMaster: true); + + if (attemptCount > consecutiveFailureCount + 1) + { + Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the next region, and the subsequent read request/s were successful on the next region."); + Assert.IsTrue(contactedRegions.Contains(region2)); + Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); + } + else + { + Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the read request succeeds before the consecutive failure count reaches the threshold, the partition didn't over to the next region, and the request was retried on the next region."); + Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2)); + + if (attemptCount > consecutiveFailureCount) + { + Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); + } + else + { + Assert.AreEqual(this.readRegionsMapping[region1], failoverInfo.Current); + } + } + } + catch (CosmosException) + { + Assert.Fail("Read Item operation should succeed."); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + + await this.TryDeleteItems(itemsList); + } + } + + [TestMethod] + [Owner("dkunda")] + [TestCategory("MultiRegion")] + [Timeout(70000)] + public async Task ReadItemAsync_WithCircuitBreakerDisabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldNotApplyPartitionLevelOverride() + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "False"); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + }; + + List itemsList = new() + { + new() { Id = "smTestId1", Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + int consecutiveFailureCount = 10; + for (int attemptCount = 1; attemptCount <= consecutiveFailureCount; attemptCount++) + { + try + { + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + Assert.IsNotNull(contactedRegions); + Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the read request succeeds after failover, the partition was failed over to the next region, after the failures reaches the threshold."); + Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2)); + } + catch (CosmosException) + { + Assert.Fail("Read Item operation should succeed."); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + await this.TryDeleteItems(itemsList); + } + } + + [TestMethod] + [Owner("dkunda")] + [TestCategory("MultiRegion")] + [Timeout(70000)] + public async Task CreateItemAsync_WithCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldNotApplyPartitionLevelOverride() + { + // Arrange. + int circuitBreakerConsecutiveFailureCount = 10; + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, $"{circuitBreakerConsecutiveFailureCount}"); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.CreateItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new() + { + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + }; + + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + try + { + // Act and Assert. + for (int attemptCount = 1; attemptCount <= circuitBreakerConsecutiveFailureCount; attemptCount++) + { + try + { + CosmosIntegrationTestObject testItem = new() { Id = "testId5", Pk = "pk5" }; + ItemResponse createResponse = await container.CreateItemAsync(testItem); + Assert.Fail("Create Item operation should not succeed."); + } + catch (CosmosException ex) + { + Assert.AreEqual( + expected: HttpStatusCode.ServiceUnavailable, + actual: ex.StatusCode); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = ex.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when a 503 Service Unavailable happens, the partition was not failed over to the next region, since writes are not supported in a single master account, when circuit breaker is enabled."); + Assert.IsTrue(contactedRegions.Contains(region1)); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + } + } + + [TestMethod] + [Owner("dkunda")] + [TestCategory("MultiMaster")] + [DataRow(ConnectionMode.Direct, "15", "10", DisplayName = "Direct Mode - Scenario whtn the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] + [DataRow(ConnectionMode.Direct, "25", "20", DisplayName = "Direct Mode - Scenario whtn the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] + [DataRow(ConnectionMode.Direct, "35", "30", DisplayName = "Direct Mode - Scenario whtn the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] + [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Gateway Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] + [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Gateway Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] + [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Gateway Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] + [Timeout(70000)] + public async Task CreateItemAsync_WithCircuitBreakerEnabledAndMultiMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( + ConnectionMode connectionMode, + string iterationCount, + string circuitBreakerConsecutiveFailureCount) + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForWrites, circuitBreakerConsecutiveFailureCount); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.CreateItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + Random random = new (); + List itemsCleanupList = new(); + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new() + { + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + Serializer = this.cosmosSystemTextJsonSerializer, + ConnectionMode = connectionMode, + }; + + try + { + // Act and Assert. + int totalIterations = int.Parse(iterationCount); + int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); + + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) + { + try + { + CosmosIntegrationTestObject testItem = new() + { + Id = $"mmTestId{random.Next()}", + Pk = $"mmpk{random.Next()}" + }; + + ItemResponse createResponse = await container.CreateItemAsync(testItem); + itemsCleanupList.Add(testItem); + + Assert.AreEqual( + expected: HttpStatusCode.Created, + actual: createResponse.StatusCode); + + PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( + globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, + isReadOnlyOrMultiMaster: true); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = createResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + Assert.IsNotNull(contactedRegions); + + if (attemptCount > consecutiveFailureCount + 1) + { + if (connectionMode == ConnectionMode.Direct) + { + Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the next region, and the subsequent write request/s were successful on the next region."); + Assert.IsTrue(contactedRegions.Contains(region2)); + } + + Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); + } + else + { + if (connectionMode == ConnectionMode.Direct) + { + Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the write requests succeeds before the consecutive failure count reaches the threshold, the partition didn't over to the next region, and the request was retried on the next region."); + Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2)); + } + if (attemptCount > consecutiveFailureCount) + { + Assert.AreEqual(this.readRegionsMapping[region2], failoverInfo.Current); + } + else + { + Assert.AreEqual(this.readRegionsMapping[region1], failoverInfo.Current); + } + } + } + catch (CosmosException) + { + Assert.Fail("Create Item operation should succeed."); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during CreateItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForWrites, null); + + foreach (CosmosIntegrationTestObject item in itemsCleanupList) + { + await this.container.DeleteItemAsync(item.Id, new PartitionKey(item.Pk)); + } + } + } + + [TestMethod] + [Owner("dkunda")] + [TestCategory("MultiMaster")] + [Timeout(70000)] + public async Task CreateAndReadItemAsync_WithCircuitBreakerEnabledAndMultiMasterAccountAndDefaultThresholdServiceUnavailableReceived_ShouldApplyPartitionLevelOverride() + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId1 = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule1 = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId1, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.CreateItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(2)) + .Build()) + .Build(); + + string serviceUnavailableRuleId2 = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule2 = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId2, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(2)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule1, serviceUnavailableRule2 }; + FaultInjector faultInjector = new FaultInjector(rules); + + List itemsCleanupList = new(); + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new() + { + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + Serializer = this.cosmosSystemTextJsonSerializer + }; + + try + { + // Act and Assert. + Random random = new (); + int totalIterations = 20; + int consecutiveFailureCountForReads = 10; + int consecutiveFailureCountForWrites = 5; + + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) + { + try + { + CosmosIntegrationTestObject testItem = new() + { + Id = $"mmTestId{random.Next()}", + Pk = $"mmpk{random.Next()}" + }; + + ItemResponse createResponse = await container.CreateItemAsync(testItem); + itemsCleanupList.Add(testItem); + + Assert.AreEqual( + expected: HttpStatusCode.Created, + actual: createResponse.StatusCode); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMappingForWrites = createResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegionsForWrite = new(contactedRegionMappingForWrites.Select(r => r.regionName)); + Assert.IsNotNull(contactedRegionsForWrite); + + if (attemptCount > consecutiveFailureCountForWrites + 1) + { + Assert.IsTrue(contactedRegionsForWrite.Count == 1, "Asserting that when the consecutive failure count reaches the write threshold, the partition was failed over to the next region, and the subsequent write request/s were successful on the next region."); + Assert.IsTrue(contactedRegionsForWrite.Contains(region2)); + } + else + { + Assert.IsTrue(contactedRegionsForWrite.Count == 2, "Asserting that when the write requests succeeds before the consecutive failure count reaches the write threshold, the partition didn't over to the next region, and the request was retried on the next region."); + Assert.IsTrue(contactedRegionsForWrite.Contains(region1) && contactedRegionsForWrite.Contains(region2)); + } + + ItemResponse readResponse = await container.ReadItemAsync( + id: testItem.Id, + partitionKey: new PartitionKey(testItem.Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMappingForReads = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegionsForReads = new(contactedRegionMappingForReads.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + Assert.IsNotNull(contactedRegionsForReads); + + if (attemptCount > consecutiveFailureCountForReads + 1) + { + Assert.IsTrue(contactedRegionsForReads.Count == 1, "Asserting that when the consecutive failure count reaches the read threshold, the partition was failed over to the next region, and the subsequent read request/s were successful on the next region."); + Assert.IsTrue(contactedRegionsForReads.Contains(region2)); + } + else + { + Assert.IsTrue(contactedRegionsForReads.Count == 2, "Asserting that when the read request succeeds before the consecutive failure count reaches the read threshold, the partition didn't over to the next region, and the request was retried on the next region."); + Assert.IsTrue(contactedRegionsForReads.Contains(region1) && contactedRegionsForReads.Contains(region2)); + } + } + catch (CosmosException ex) + { + Assert.Fail($"Create and Read Item operations should succeed. Message: { ex.Message}"); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during CreateItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + + foreach (CosmosIntegrationTestObject item in itemsCleanupList) + { + await this.container.DeleteItemAsync(item.Id, new PartitionKey(item.Pk)); + } + } + } + + [TestMethod] + [Owner("dkunda")] + [TestCategory("MultiRegion")] + [Timeout(70000)] + [DataRow(true, DisplayName = "Test scenario when PPAF is enabled at client level.")] + [DataRow(false, DisplayName = "Test scenario when PPAF is disabled at client level.")] + public async Task ReadItemAsync_WithPPAFEnabledAndSingleMasterAccountWithResponseDelay_ShouldHedgeRequestToMultipleRegions( + bool enablePartitionLevelFailover) + { + // Arrange. + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ResponseDelay) + .WithDelay(TimeSpan.FromMilliseconds(3000)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + // Now that the ppaf enablement flag is returned from gateway, we need to intercept the response and remove the flag from the response, so that + // the environment variable set above is honored. + HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper() + { + ResponseIntercepter = async (response, request) => + { + string json = await response?.Content?.ReadAsStringAsync(); + if (json.Length > 0 && json.Contains("enablePerPartitionFailoverBehavior")) + { + JObject parsedDatabaseAccountResponse = JObject.Parse(json); + parsedDatabaseAccountResponse.Property("enablePerPartitionFailoverBehavior").Value = enablePartitionLevelFailover.ToString(); + + HttpResponseMessage interceptedResponse = new() + { + StatusCode = response.StatusCode, + Content = new StringContent(parsedDatabaseAccountResponse.ToString()), + Version = response.Version, + ReasonPhrase = response.ReasonPhrase, + RequestMessage = response.RequestMessage, + }; + + return interceptedResponse; + } + + return response; + }, + }; + + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + HttpClientFactory = () => new HttpClient(httpClientHandlerHelper), + }; + + List itemsList = new() + { + new() { Id = "smTestId1", Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + CosmosTraceDiagnostics traceDiagnostic = readResponse.Diagnostics as CosmosTraceDiagnostics; + Assert.IsNotNull(traceDiagnostic); + + traceDiagnostic.Value.Data.TryGetValue("Hedge Context", out object hedgeContext); + + if (enablePartitionLevelFailover) + { + Assert.IsNotNull(hedgeContext); + List hedgedRegions = ((IEnumerable)hedgeContext).ToList(); + + Assert.IsTrue(hedgedRegions.Count > 1, "Since the first region is not available, the request should atleast hedge to the next region."); + Assert.IsTrue(hedgedRegions.Contains(region1) && (hedgedRegions.Contains(region2) || hedgedRegions.Contains(region3))); + } + else + { + Assert.IsNull(hedgeContext); + } + + Assert.IsNotNull(contactedRegions); + Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the read request succeeds on any region, given that there were no availability loss."); + } + finally + { + await this.TryDeleteItems(itemsList); + } + } + + [TestMethod] + [Owner("ntripician")] + [TestCategory("MultiRegion")] + [Timeout(70000 *100)] + [DataRow(ConnectionMode.Direct, false, DisplayName = "Test dynamic PPAF enablement with Direct mode.")] + public async Task ReadItemAsync_WithPPAFDynamicOverride_ShouldEnableOrDisablePPAFInSDK( + ConnectionMode connectionMode, + bool isThinClientEnabled) + { + // Arrange. + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + bool enablePPAF = false; + + // Now that the ppaf enablement flag is returned from gateway, we need to intercept the response and remove the flag from the response, so that + // the environment variable set above is honored. + HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper() + { + ResponseIntercepter = async (response, request) => + { + string json = await response?.Content?.ReadAsStringAsync(); + if (json.Length > 0 && json.Contains("enablePerPartitionFailoverBehavior")) + { + if (enablePPAF) + { + JObject parsedDatabaseAccountResponse = JObject.Parse(json); + parsedDatabaseAccountResponse.Property("enablePerPartitionFailoverBehavior").Value = true; + + HttpResponseMessage interceptedResponse = new() + { + StatusCode = response.StatusCode, + Content = new StringContent(parsedDatabaseAccountResponse.ToString()), + Version = response.Version, + ReasonPhrase = response.ReasonPhrase, + RequestMessage = response.RequestMessage, + }; + + return interceptedResponse; + } + else + { + JObject parsedDatabaseAccountResponse = JObject.Parse(json); + parsedDatabaseAccountResponse.Property("enablePerPartitionFailoverBehavior").Value = false; + + HttpResponseMessage interceptedResponse = new() + { + StatusCode = response.StatusCode, + Content = new StringContent(parsedDatabaseAccountResponse.ToString()), + Version = response.Version, + ReasonPhrase = response.ReasonPhrase, + RequestMessage = response.RequestMessage, + }; + + return interceptedResponse; + } + + } + + return response; + }, + }; + + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + HttpClientFactory = () => new HttpClient(httpClientHandlerHelper), + ConnectionMode = connectionMode, + ApplicationName = "ppafDynamicOverrideTest", + }; + + List itemsList = new() + { + new() { Id = "smTestId1", Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + CosmosTraceDiagnostics traceDiagnostic = readResponse.Diagnostics as CosmosTraceDiagnostics; + Assert.IsNotNull(traceDiagnostic); + + traceDiagnostic.Value.Data.TryGetValue("Hedge Context", out object hedgeContextNoPPAF); + + Assert.IsNull(hedgeContextNoPPAF); + Assert.IsNull(cosmosClient.DocumentClient.ConnectionPolicy.AvailabilityStrategy); + Assert.IsFalse(cosmosClient.DocumentClient.PartitionKeyRangeLocation.IsPartitionLevelAutomaticFailoverEnabled()); + + // Enable PPAF At the Gateway Layer. + enablePPAF = true; + + //force database account refresh + await cosmosClient.DocumentClient.GlobalEndpointManager.RefreshLocationAsync(true); + + readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + traceDiagnostic = readResponse.Diagnostics as CosmosTraceDiagnostics; + Assert.IsNotNull(traceDiagnostic); + + traceDiagnostic.Value.Data.TryGetValue("Hedge Context", out object hedgeContext); + + Assert.IsNotNull(hedgeContext); + List hedgedRegions = ((IEnumerable)hedgeContext).ToList(); + + Assert.IsTrue(hedgedRegions.Count >= 1, "Since the first region is not available, the request should atleast hedge to the next region."); + Assert.IsTrue(cosmosClient.DocumentClient.PartitionKeyRangeLocation.IsPartitionLevelAutomaticFailoverEnabled()); + + // Disable PPAF At the Gateway Layer. + enablePPAF = false; + + //force database account refresh + await cosmosClient.DocumentClient.GlobalEndpointManager.RefreshLocationAsync(true); + + readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + traceDiagnostic = readResponse.Diagnostics as CosmosTraceDiagnostics; + Assert.IsNotNull(traceDiagnostic); + + traceDiagnostic.Value.Data.TryGetValue("Hedge Context", out object hedgeContextNoPPAF2); + + Assert.IsNull(hedgeContextNoPPAF2); + Assert.IsNull(cosmosClient.DocumentClient.ConnectionPolicy.AvailabilityStrategy); + Assert.IsFalse(cosmosClient.DocumentClient.PartitionKeyRangeLocation.IsPartitionLevelAutomaticFailoverEnabled()); + } + finally + { + await this.TryDeleteItems(itemsList); + + if (isThinClientEnabled) + { + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, null); + } + } + } + + [TestMethod] + [Owner("nalutripician")] + [TestCategory("MultiRegion")] + [Timeout(70000)] + [DataRow(true, DisplayName = "Test scenario when PPAF is enabled at client level.")] + [DataRow(false, DisplayName = "Test scenario when PPAF is disabled at client level.")] + public async Task ReadItemAsync_WithPPAFDiableOverride( + bool enablePartitionLevelFailover) + { + // Arrange. + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ResponseDelay) + .WithDelay(TimeSpan.FromMilliseconds(3000)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + // Now that the ppaf enablement flag is returned from gateway, we need to intercept the response and remove the flag from the response, so that + // the environment variable set above is honored. + HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper() + { + ResponseIntercepter = async (response, request) => + { + string json = await response?.Content?.ReadAsStringAsync(); + if (json.Length > 0 && json.Contains("enablePerPartitionFailoverBehavior")) + { + JObject parsedDatabaseAccountResponse = JObject.Parse(json); + parsedDatabaseAccountResponse.Property("enablePerPartitionFailoverBehavior").Value = enablePartitionLevelFailover.ToString(); + + HttpResponseMessage interceptedResponse = new() + { + StatusCode = response.StatusCode, + Content = new StringContent(parsedDatabaseAccountResponse.ToString()), + Version = response.Version, + ReasonPhrase = response.ReasonPhrase, + RequestMessage = response.RequestMessage, + }; + + return interceptedResponse; + } + + return response; + }, + }; + + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + HttpClientFactory = () => new HttpClient(httpClientHandlerHelper), + DisablePartitionLevelFailover = true, // This will disable the PPAF override for this test. + }; + + List itemsList = new() + { + new() { Id = "smTestId1", Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + CosmosTraceDiagnostics traceDiagnostic = readResponse.Diagnostics as CosmosTraceDiagnostics; + Assert.IsNotNull(traceDiagnostic); + + traceDiagnostic.Value.Data.TryGetValue("Hedge Context", out object hedgeContext); + + Assert.IsNull(hedgeContext); + + Assert.IsNotNull(contactedRegions); + Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the read request succeeds on any region, given that there were no availability loss."); + } + finally + { + await this.TryDeleteItems(itemsList); + } + } + + [TestMethod] + [TestCategory("MultiRegion")] + [Owner("ntripician")] + public async Task AddressRefreshInternalServerErrorTest() + { + FaultInjectionRule internalServerError = new FaultInjectionRuleBuilder( + id: "rule1", + condition: new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.MetadataRefreshAddresses) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.InternalServerError) + .Build()) + .Build(); + + List rules = new List() { internalServerError }; + FaultInjector faultInjector = new FaultInjector(rules); + + internalServerError.Disable(); + + CosmosClientOptions clientOptions = new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Direct, + Serializer = this.cosmosSystemTextJsonSerializer, + ApplicationRegion = region1, + }; + + using (CosmosClient faultInjectionClient = new CosmosClient( + connectionString: this.connectionString, + clientOptions: faultInjector.GetFaultInjectionClientOptions(clientOptions))) + { + Database database = faultInjectionClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + internalServerError.Enable(); + + try + { + ItemResponse response = await container.ReadItemAsync("testId", new PartitionKey("pk")); + Assert.IsTrue(internalServerError.GetHitCount() > 0); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + } + catch (CosmosException ex) + { + Assert.Fail(ex.Message); + } + } + } + + [TestMethod] + [TestCategory("MultiRegion")] + [Ignore("We will enable this test once the test staging account used for multi master validation starts supporting thin proxy.")] + [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] + [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] + [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] + [Owner("dkunda")] + [Timeout(70000)] + public async Task ReadItemAsync_WithThinClientCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( + ConnectionMode connectionMode, + string iterationCount, + string circuitBreakerConsecutiveFailureCount) + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(Regions.WestUS) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + List preferredRegions = new List { Regions.WestUS, Regions.EastAsia }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConnectionMode = connectionMode, + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + }; + + List itemsList = new() + { + new() { Id = "smTestId1", Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + AccountProperties accountInfo = await cosmosClient.ReadAccountAsync(); + + Assert.IsTrue(cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints.Count() >= 2); + this.thinClientreadRegionalEndpoints = cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints; + + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); + int totalIterations = int.Parse(iterationCount); + + for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) + { + try + { + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + Assert.IsNotNull(contactedRegions); + + PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( + globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, + isReadOnlyOrMultiMaster: true); + + if (attemptCount > consecutiveFailureCount) + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); + } + else + { + if (attemptCount == consecutiveFailureCount) + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); + } + else + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[0], failoverInfo.Current); + } + } + } + catch (CosmosException ce) + { + Assert.Fail("Read Item operation should succeed." + ce); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, null); + + await this.TryDeleteItems(itemsList); + } + } + + [TestMethod] + [TestCategory("MultiMaster")] + [Ignore ("We will enable this test once the test staging account used for multi master validation starts supporting thin proxy.")] + [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] + [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] + [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] + [Owner("dkunda")] + [Timeout(70000)] + public async Task CreateItemAsync_WithThinClientEnabledAndCircuitBreakerEnabledAndMultiMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( + ConnectionMode connectionMode, + string iterationCount, + string circuitBreakerConsecutiveFailureCount) + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.CreateItem) + .WithRegion(Regions.WestUS) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + Random random = new(); + List itemsCleanupList = new(); + List preferredRegions = new List { Regions.WestUS, Regions.EastAsia }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConnectionMode = connectionMode, + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + }; + + List itemsList = new() + { + new() { Id = "smTestId1", Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + AccountProperties accountInfo = await cosmosClient.ReadAccountAsync(); + + Assert.IsTrue(cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints.Count() >= 2); + this.thinClientreadRegionalEndpoints = cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints; + + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); + int totalIterations = int.Parse(iterationCount); + + for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) + { + try + { + CosmosIntegrationTestObject testItem = new() + { + Id = $"mmTestId{random.Next()}", + Pk = $"mmpk{random.Next()}" + }; + + ItemResponse createResponse = await container.CreateItemAsync(testItem); + itemsCleanupList.Add(testItem); + + Assert.AreEqual( + expected: HttpStatusCode.Created, + actual: createResponse.StatusCode); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = createResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: createResponse.StatusCode); + + Assert.IsNotNull(contactedRegions); + + PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( + globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, + isReadOnlyOrMultiMaster: true); + + if (attemptCount > consecutiveFailureCount) + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); + } + else + { + if (attemptCount == consecutiveFailureCount) + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); + } + else + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[0], failoverInfo.Current); + } + } + } + catch (CosmosException ce) + { + Assert.Fail("Create Item operation should succeed." + ce); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during CreateItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, null); + + await this.TryDeleteItems(itemsList); + } + } + + [TestMethod] + [Owner("ntripician")] + [TestCategory("MultiRegion")] + [Timeout(70000)] + public async Task ClinetOverrides0msRequestTimeoutValueForPPAF() + { + // Arrange. + + // Now that the ppaf enablement flag is returned from gateway, we need to intercept the response and remove the flag from the response, so that + // the environment variable set above is honored. + HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper() + { + ResponseIntercepter = async (response, request) => + { + string json = await response?.Content?.ReadAsStringAsync(); + if (json.Length > 0 && json.Contains("enablePerPartitionFailoverBehavior")) + { + JObject parsedDatabaseAccountResponse = JObject.Parse(json); + parsedDatabaseAccountResponse.Property("enablePerPartitionFailoverBehavior").Value = "true"; + + HttpResponseMessage interceptedResponse = new() + { + StatusCode = response.StatusCode, + Content = new StringContent(parsedDatabaseAccountResponse.ToString()), + Version = response.Version, + ReasonPhrase = response.ReasonPhrase, + RequestMessage = response.RequestMessage, + }; + + return interceptedResponse; + } + + return response; + }, + }; + + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConsistencyLevel = ConsistencyLevel.Session, + RequestTimeout = TimeSpan.FromSeconds(0), + ApplicationPreferredRegions = preferredRegions, + HttpClientFactory = () => new HttpClient(httpClientHandlerHelper), + }; + + + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + try + { + //request to start document client initiation + _ = await container.ReadItemAsync("id", new PartitionKey("pk1")); + } + catch { } + + // Act and Assert. + + CrossRegionHedgingAvailabilityStrategy strat = cosmosClient.DocumentClient.ConnectionPolicy.AvailabilityStrategy as CrossRegionHedgingAvailabilityStrategy; + Assert.IsNotNull(strat); + Assert.AreNotEqual(0, strat.Threshold); + } + + + [TestMethod] + [TestCategory("MultiRegion")] + [Owner("trivediyash")] + [Description("Scenario: When a document is created, then updated, and finally deleted, the operations must reflect on Change Feed.")] + public async Task WhenADocumentIsCreatedThenUpdatedThenDeletedCFPTests() + { + string testId = "testDoc" + Guid.NewGuid().ToString("N"); + string testPk = "testPk" + Guid.NewGuid().ToString("N"); + + try + { + // Create the document + CosmosIntegrationTestObject createItem = new CosmosIntegrationTestObject + { + Id = testId, + Pk = testPk, + Other = "original test" + }; + + ItemResponse createResponse = await this.container.CreateItemAsync( + createItem, + new PartitionKey(testPk)); + + Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); + Assert.IsNotNull(createResponse.Resource); + Assert.AreEqual(testId, createResponse.Resource.Id); + Assert.AreEqual(testPk, createResponse.Resource.Pk); + Assert.AreEqual("original test", createResponse.Resource.Other); + + // Wait 1 second to ensure different timestamps + await Task.Delay(1000); + + // Update the document + CosmosIntegrationTestObject updateItem = new CosmosIntegrationTestObject + { + Id = testId, + Pk = testPk, + Other = "test after replace" + }; + + ItemResponse updateResponse = await this.container.ReplaceItemAsync( + updateItem, + testId, + new PartitionKey(testPk)); + + Assert.AreEqual(HttpStatusCode.OK, updateResponse.StatusCode); + Assert.IsNotNull(updateResponse.Resource); + Assert.AreEqual(testId, updateResponse.Resource.Id); + Assert.AreEqual(testPk, updateResponse.Resource.Pk); + Assert.AreEqual("test after replace", updateResponse.Resource.Other); + + // Verify the ETag changed + Assert.AreNotEqual(createResponse.ETag, updateResponse.ETag); + + // Wait 1 second to ensure different timestamps + await Task.Delay(1000); + + // Delete the document + ItemResponse deleteResponse = await this.container.DeleteItemAsync( + testId, + new PartitionKey(testPk)); + + Assert.AreEqual(HttpStatusCode.NoContent, deleteResponse.StatusCode); + + // Verify the document no longer exists + try + { + await this.container.ReadItemAsync(testId, new PartitionKey(testPk)); + Assert.Fail("Document should not exist after deletion"); + } + catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + // Expected - document was successfully deleted + } + } + finally + { + // Cleanup in case test failed before deletion + try + { + await this.container.DeleteItemAsync(testId, new PartitionKey(testPk)); + } + catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + // Ignore - document already deleted + } + } + } + + [TestMethod] + [TestCategory("MultiRegion")] + [Owner("pkolluri")] + [Timeout(70000)] + public async Task QueryItemAsync_WithCircuitBreakerEnabledMultiRegionAndServiceResponseDelay_ShouldFailOverToNextRegionAsync() + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, "1"); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceResponseDelayRuleId = "response-delay-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceResponseDelayRuleFromRegion1 = new FaultInjectionRuleBuilder( + id: serviceResponseDelayRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.QueryItem) + .WithConnectionType(FaultInjectionConnectionType.Gateway) + .WithRegion(region1) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ResponseDelay) + .WithDelay(TimeSpan.FromSeconds(70)) + .Build()) + .Build(); + + serviceResponseDelayRuleFromRegion1.Disable(); + + List rules = new List { serviceResponseDelayRuleFromRegion1}; + FaultInjector faultInjector = new FaultInjector(rules); + + List preferredRegions = new List { region1, region2, region3 }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + ApplicationPreferredRegions = preferredRegions, + ConnectionMode = ConnectionMode.Gateway, + }; + + List itemsList = new() + { + new() { Id = "smTestId2", Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + bool isRegion1Available = true; + bool isRegion2Available = true; + + int thresholdCounter = 0; + int totalIterations = 7; + int ppcbDefaultThreshold = 1; + int firstRegionServiceUnavailableAttempt = 1; + + for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) + { + try + { + string sqlQueryText = $"SELECT * FROM c WHERE c.id = '{itemsList[0].Id}'"; + using FeedIterator feedIterator = container.GetItemQueryIterator(sqlQueryText, requestOptions: new QueryRequestOptions()); + + while (feedIterator.HasMoreResults) + { + FeedResponse response = await feedIterator.ReadNextAsync(); + Assert.AreEqual(System.Net.HttpStatusCode.OK, response.StatusCode); + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = response.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + if (isRegion1Available && isRegion2Available) + { + Assert.IsTrue(contactedRegions.Count == 1, "Assert that, when no failure happened, the query request is being served from region 1."); + Assert.IsTrue(contactedRegions.Contains(region1)); + + // Simulating service unavailable on region 1. + if (attemptCount == firstRegionServiceUnavailableAttempt) + { + isRegion1Available = false; + serviceResponseDelayRuleFromRegion1.Enable(); + } + } + else if (isRegion2Available) + { + if (thresholdCounter <= ppcbDefaultThreshold) + { + Assert.IsTrue(contactedRegions.Count == 2, "Asserting that when the query request succeeds before the consecutive failure count reaches the threshold, the partition didn't fail over to the next region, and the request was retried."); + Assert.IsTrue(contactedRegions.Contains(region1) && contactedRegions.Contains(region2), "Asserting that both region 1 and region 2 were contacted."); + thresholdCounter++; + } + else + { + Assert.IsTrue(contactedRegions.Count == 1, "Asserting that when the consecutive failure count reaches the threshold, the partition was failed over to the next region, and the subsequent query request/s were successful on the next region"); + } + } + } + } + catch (CosmosException ce) + { + Assert.Fail("Query operation should succeed with successful failover to next region." + ce.Diagnostics.ToString()); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during Query operation call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + + await this.TryDeleteItems(itemsList); + } + } + + private async Task TryCreateItems(List testItems) + { + foreach (CosmosIntegrationTestObject item in testItems) + { + await this.TryCreateItem(item); + } + } + + private async Task TryCreateItem(CosmosIntegrationTestObject testItem) + { + try + { + await this.container.CreateItemAsync(testItem); + } + catch (CosmosException ce) + { + Assert.Fail($"Failed to create item with id: {testItem.Id}, message: {ce.Message}"); + } + } + + private async Task TryDeleteItems(List testItems) + { + foreach (CosmosIntegrationTestObject item in testItems) + { + await this.TryDeleteItem(item); + } } - - private async Task TryCreateItems(List testItems) - { - foreach (CosmosIntegrationTestObject item in testItems) - { - await this.TryCreateItem(item); - } - } - - private async Task TryCreateItem(CosmosIntegrationTestObject testItem) - { - try - { - await this.container.CreateItemAsync(testItem); - } - catch (CosmosException ce) - { - Assert.Fail($"Failed to create item with id: {testItem.Id}, message: {ce.Message}"); - } - } - - private async Task TryDeleteItems(List testItems) - { - foreach (CosmosIntegrationTestObject item in testItems) - { - await this.TryDeleteItem(item); - } - } - - private async Task TryDeleteItem(CosmosIntegrationTestObject testItem) - { - try - { - await this.container.DeleteItemAsync( - testItem.Id, - new PartitionKey(testItem.Pk)); - } - catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) - { - // Ignore - } + + private async Task TryDeleteItem(CosmosIntegrationTestObject testItem) + { + try + { + await this.container.DeleteItemAsync( + testItem.Id, + new PartitionKey(testItem.Pk)); + } + catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + // Ignore + } } public sealed class TestCosmosItem @@ -2079,6 +2549,6 @@ public TestCosmosItem( public DateTime CreatedUtc { get; } public DateTime ModifiedUtc { get; } public DateTime[] ExtraDates { get; } - } - } -} + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs index 0cc2eb1eef..379de2faba 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs @@ -20,6 +20,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests using System.Threading.Tasks; using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Diagnostics; + using Microsoft.Azure.Cosmos.Handlers; using Microsoft.Azure.Cosmos.Json; using Microsoft.Azure.Cosmos.Query.Core.ExecutionContext; using Microsoft.Azure.Cosmos.Query.Core.QueryClient; @@ -39,7 +40,8 @@ public class CosmosItemTests : BaseCosmosClientHelper { private Container Container = null; private ContainerProperties containerSettings = null; - + + private const string HubRegionHeader = "x-ms-cosmos-hub-region-processing-only"; private static readonly string nonPartitionItemId = "fixed-Container-Item"; private static readonly string undefinedPartitionItemId = "undefined-partition-Item"; @@ -275,7 +277,7 @@ public async Task NegativeCreateItemTest(bool binaryEncodingEnabledInClient) { Assert.AreEqual(999999, ce.SubStatusCode); string exception = ce.ToString(); - Assert.IsTrue(exception.StartsWith("Microsoft.Azure.Cosmos.CosmosException : Response status code does not indicate success: Forbidden (403); Substatus: 999999; ")); + Assert.IsTrue(exception.Contains("Response status code does not indicate success: Forbidden (403); Substatus: 999999; ")); string diagnostics = ce.Diagnostics.ToString(); Assert.IsTrue(diagnostics.Contains("999999")); CosmosItemTests.ValidateCosmosException(ce); @@ -2173,7 +2175,7 @@ public async Task ItemEpkQuerySingleKeyRangeValidation() //new List> { new Documents.Routing.Range("AA", "AA", true, true) }, containerResponse.Resource.PartitionKey, vectorEmbeddingPolicy: null, - containerResponse.Resource.GeospatialConfig.GeospatialType); + containerResponse.Resource.GeospatialConfig.GeospatialType, false); // There should only be one range since the EPK option is set. List partitionKeyRanges = await CosmosQueryExecutionContextFactory.GetTargetPartitionKeyRangesAsync( @@ -4315,7 +4317,112 @@ private static async Task GivenItemAsyncWhenMissingMemberHandlingIsErrorThenExpe JsonConvert.DefaultSettings = () => default; } - } + } + + [TestMethod] + [Owner("aavasthy")] + [Description("Forces two consecutive 404/1002 responses from the gateway and verifies ClientRetryPolicy sets the hub region header flag after the first retry fails.")] + public async Task ReadItemAsync_ShouldAddHubHeader_OnRetryAfter_404_1002() + { + int requestCount = 0; + int return404Count = 0; + const int maxReturn404 = 2; // Return 404/1002 twice + + // Created HTTP handler to intercept requests + HttpClientHandlerHelper httpHandler = new HttpClientHandlerHelper + { + RequestCallBack = (request, cancellationToken) => + { + // Track all document read requests + if (request.Method == HttpMethod.Get && + request.RequestUri != null && + request.RequestUri.AbsolutePath.Contains("/docs/")) + { + requestCount++; + + // Header should NOT be present on first retry (2nd request) + if (requestCount == 2 && + request.Headers.TryGetValues(HubRegionHeader, out IEnumerable firstRetryValues) && + firstRetryValues.Any()) + { + Assert.Fail("Header should NOT be present on first retry attempt."); + } + + // Return fake 404/1002 for first two requests + if (return404Count < maxReturn404) + { + return404Count++; + + var errorResponse = new + { + code = "NotFound", + message = "Message: {\"Errors\":[\"Resource Not Found. Learn more: https://aka.ms/cosmosdb-tsg-not-found\"]}\r\nActivityId: " + Guid.NewGuid() + ", Request URI: " + request.RequestUri, + additionalErrorInfo = "" + }; + + HttpResponseMessage notFoundResponse = new HttpResponseMessage(HttpStatusCode.NotFound) + { + Content = new StringContent( + JsonConvert.SerializeObject(errorResponse), + Encoding.UTF8, + "application/json" + ) + }; + + // Add the substatus header for ReadSessionNotAvailable + notFoundResponse.Headers.Add("x-ms-substatus", "1002"); + notFoundResponse.Headers.Add("x-ms-activity-id", Guid.NewGuid().ToString()); + notFoundResponse.Headers.Add("x-ms-request-charge", "1.0"); + + return Task.FromResult(notFoundResponse); + } + } + + return Task.FromResult(null); + } + }; + + CosmosClientOptions clientOptions = new CosmosClientOptions + { + ConnectionMode = ConnectionMode.Gateway, + ConsistencyLevel = Cosmos.ConsistencyLevel.Session, + HttpClientFactory = () => new HttpClient(httpHandler), + MaxRetryAttemptsOnRateLimitedRequests = 9, + MaxRetryWaitTimeOnRateLimitedRequests = TimeSpan.FromSeconds(30) + }; + + using CosmosClient customClient = TestCommon.CreateCosmosClient(clientOptions); + + Container customContainer = customClient.GetContainer(this.database.Id, this.Container.Id); + + // Create a test item first + ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity(); + await this.Container.CreateItemAsync(testItem, new Cosmos.PartitionKey(testItem.pk)); + + try + { + // This should trigger 404/1002 twice + // In single-region emulator, after first retry fails with 404/1002, it won't retry again + ItemResponse response = await customContainer.ReadItemAsync( + testItem.id, + new Cosmos.PartitionKey(testItem.pk)); + + Assert.Fail("Expected CosmosException due to consecutive 404/1002 failures."); + } + catch (CosmosException ex) + { + // Expected: After first retry fails with 404/1002, single master won't retry again + Assert.AreEqual(HttpStatusCode.NotFound, ex.StatusCode); + Assert.AreEqual((int)SubStatusCodes.ReadSessionNotAvailable, ex.SubStatusCode); + } + + // Verify the expected behavior: + // 1. Initial request (requestCount = 1) fails with 404/1002 + // 2. First retry (requestCount = 2) fails with 404/1002 + // 3. No more retries because single master + no additional regions + Assert.AreEqual(2, requestCount, $"Expected exactly 2 requests (initial + 1 retry) for single-region emulator, but got {requestCount}"); + Assert.AreEqual(2, return404Count, "Both requests should have returned 404/1002"); + } private async Task AutoGenerateIdPatternTest(Cosmos.PartitionKey pk, T itemWithoutId) { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemThinClientTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemThinClientTests.cs new file mode 100644 index 0000000000..7cbd053101 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemThinClientTests.cs @@ -0,0 +1,1036 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Net; + using System.Net.Http; + using System.Text.Json; + using System.Text.Json.Serialization; + using System.Threading; + using System.Threading.Tasks; + using global::Azure; + using global::Azure.Core; + using Microsoft.Azure.Cosmos.Fluent; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using static Microsoft.Azure.Cosmos.SDK.EmulatorTests.MultiRegionSetupHelpers; + using TestObject = MultiRegionSetupHelpers.CosmosIntegrationTestObject; + + [TestClass] + public class CosmosItemThinClientTests + { + private string connectionString; + private CosmosClient client; + private Database database; + private Container container; + private MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer cosmosSystemTextJsonSerializer; + private const int ItemCount = 100; + + [TestInitialize] + public async Task TestInitAsync() + { + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); + this.connectionString = Environment.GetEnvironmentVariable("COSMOSDB_THINCLIENT"); + + if (string.IsNullOrEmpty(this.connectionString)) + { + Assert.Fail("Set environment variable COSMOSDB_THINCLIENT to run the tests"); + } + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = null, + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + this.cosmosSystemTextJsonSerializer = new MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer(jsonSerializerOptions); + + this.client = new CosmosClient( + this.connectionString, + new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Gateway, + Serializer = this.cosmosSystemTextJsonSerializer, + }); + + string uniqueDbName = "TestDb_" + Guid.NewGuid().ToString(); + this.database = await this.client.CreateDatabaseIfNotExistsAsync(uniqueDbName); + string uniqueContainerName = "TestContainer_" + Guid.NewGuid().ToString(); + this.container = await this.database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk"); + } + + [TestCleanup] + public async Task TestCleanupAsync() + { + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "False"); + + if (this.database != null) + { + await this.database.DeleteAsync(); + } + + if (this.client != null) + { + this.client.Dispose(); + } + } + + private IEnumerable GenerateItems(string partitionKey) + { + List items = new List(); + for (int i = 0; i < ItemCount; i++) + { + items.Add(new TestObject + { + Id = Guid.NewGuid().ToString(), + Pk = partitionKey, + Other = "Test Item " + i + }); + } + return items; + } + + private async Task> CreateItemsSafeAsync(IEnumerable items) + { + List itemsCreated = new List(); + foreach (TestObject item in items) + { + try + { + ItemResponse response = await this.container.CreateItemAsync(item, new PartitionKey(item.Pk)); + if (response.StatusCode == HttpStatusCode.Created) + { + itemsCreated.Add(item); + } + } + catch (CosmosException) + { + } + } + return itemsCreated; + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task RegionalDatabaseAccountNameIsEmptyInPayload() + { + byte[] capturedPayload = null; + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); + + // Initialize the serializer locally + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = null, + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + CosmosSystemTextJsonSerializer serializer = new CosmosSystemTextJsonSerializer(jsonSerializerOptions); + + CosmosClientBuilder builder = new CosmosClientBuilder(this.connectionString) + .WithConnectionModeGateway() + .WithCustomSerializer(serializer) + .WithSendingRequestEventArgs(async (sender, e) => + { + if (e.HttpRequest.Version == new Version(2, 0)) + { + if (e.HttpRequest.Content != null) + { + capturedPayload = await e.HttpRequest.Content.ReadAsByteArrayAsync(); + } + } + }); + + using CosmosClient client = builder.Build(); + string uniqueDbName = "TestRegional_" + Guid.NewGuid().ToString(); + Database database = await client.CreateDatabaseIfNotExistsAsync(uniqueDbName); + string uniqueContainerName = "TestRegionalContainer_" + Guid.NewGuid().ToString(); + Container container = await database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk"); + + string pk = "pk_regional"; + TestObject testItem = this.GenerateItems(pk).First(); + + // Act + ItemResponse response = await container.CreateItemAsync(testItem, new PartitionKey(testItem.Pk)); + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); + + // Assert + Assert.IsNotNull(capturedPayload, "The request payload was not captured."); + + + // The RNTBD protocol serializes an empty string as a token with a length of 0. + // For `regionalDatabaseAccountName`, which is a SmallString (type 0x02), this is + // serialized as two bytes: 0x02 (type) and 0x00 (length). + // This byte pair represents an empty string value in RNTBD’s small-string encoding. + byte[] emptyStringToken = { 0x02, 0x00 }; + + bool foundEmptyStringToken = false; + for (int i = 0; i <= capturedPayload.Length - emptyStringToken.Length; i++) + { + if (capturedPayload[i] == emptyStringToken[0] && capturedPayload[i + 1] == emptyStringToken[1]) + { + foundEmptyStringToken = true; + break; + } + } + + Assert.IsTrue(foundEmptyStringToken, "The RNTBD payload should contain a token representing an empty string for the regional account name."); + + // Cleanup + await database.DeleteAsync(); + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task TestThinClientWithExecuteStoredProcedureAsync() + { + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "true"); + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = null, + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + this.cosmosSystemTextJsonSerializer = new MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer(jsonSerializerOptions); + + this.client = new CosmosClient( + this.connectionString, + new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Gateway, + Serializer = this.cosmosSystemTextJsonSerializer, + }); + + string uniqueDbName = "TestDbStoreProc_" + Guid.NewGuid().ToString(); + this.database = await this.client.CreateDatabaseIfNotExistsAsync(uniqueDbName); + string uniqueContainerName = "TestDbStoreProcContainer_" + Guid.NewGuid().ToString(); + this.container = await this.database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk"); + + + string sprocId = "testSproc_" + Guid.NewGuid().ToString(); + string sprocBody = @"function(itemToCreate) { + var context = getContext(); + var collection = context.getCollection(); + var response = context.getResponse(); + + if (!itemToCreate) throw new Error('Item is undefined or null.'); + + // Create a document + var accepted = collection.createDocument( + collection.getSelfLink(), + itemToCreate, + function(err, newItem) { + if (err) throw err; + + // Query the created document + var query = 'SELECT * FROM c WHERE c.id = ""' + newItem.id + '""'; + var isAccepted = collection.queryDocuments( + collection.getSelfLink(), + query, + function(queryErr, documents) { + if (queryErr) throw queryErr; + response.setBody({ + created: newItem, + queried: documents[0] + }); + } + ); + if (!isAccepted) throw 'Query not accepted'; + }); + + if (!accepted) throw new Error('Create was not accepted.'); + }"; + + // Create stored procedure + Scripts.StoredProcedureResponse createResponse = await this.container.Scripts.CreateStoredProcedureAsync( + new Scripts.StoredProcedureProperties(sprocId, sprocBody)); + Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); + + // Execute stored procedure + string testPartitionId = Guid.NewGuid().ToString(); + TestObject testItem = new TestObject + { + Id = Guid.NewGuid().ToString(), + Pk = testPartitionId, + Other = "Created by Stored Procedure" + }; + + Scripts.StoredProcedureExecuteResponse executeResponse = + await this.container.Scripts.ExecuteStoredProcedureAsync( + sprocId, + new PartitionKey(testPartitionId), + new dynamic[] { testItem }); + + Assert.AreEqual(HttpStatusCode.OK, executeResponse.StatusCode); + Assert.IsNotNull(executeResponse.Resource); + string diagnostics = executeResponse.Diagnostics.ToString(); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + + // Delete stored procedure + await this.container.Scripts.DeleteStoredProcedureAsync(sprocId); + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task TestThinClientWithExecuteStoredProcedureStreamAsync() + { + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "true"); + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = null, + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + this.cosmosSystemTextJsonSerializer = new MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer(jsonSerializerOptions); + + this.client = new CosmosClient( + this.connectionString, + new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Gateway, + Serializer = this.cosmosSystemTextJsonSerializer, + }); + + string uniqueDbName = "TestDbStoreProc_" + Guid.NewGuid().ToString(); + this.database = await this.client.CreateDatabaseIfNotExistsAsync(uniqueDbName); + string uniqueContainerName = "TestDbStoreProcContainer_" + Guid.NewGuid().ToString(); + this.container = await this.database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk"); + + + string sprocId = "testSproc_" + Guid.NewGuid().ToString(); + string sprocBody = @"function(itemToCreate) { + var context = getContext(); + var collection = context.getCollection(); + var response = context.getResponse(); + + if (!itemToCreate) throw new Error('Item is undefined or null.'); + + // Create a document + var accepted = collection.createDocument( + collection.getSelfLink(), + itemToCreate, + function(err, newItem) { + if (err) throw err; + + // Query the created document + var query = 'SELECT * FROM c WHERE c.id = ""' + newItem.id + '""'; + var isAccepted = collection.queryDocuments( + collection.getSelfLink(), + query, + function(queryErr, documents) { + if (queryErr) throw queryErr; + response.setBody({ + created: newItem, + queried: documents[0] + }); + } + ); + if (!isAccepted) throw 'Query not accepted'; + }); + + if (!accepted) throw new Error('Create was not accepted.'); + }"; + + // Create stored procedure + Scripts.StoredProcedureResponse createResponse = await this.container.Scripts.CreateStoredProcedureAsync( + new Scripts.StoredProcedureProperties(sprocId, sprocBody)); + Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); + + // Execute stored procedure + string testPartitionId = Guid.NewGuid().ToString(); + TestObject testItem = new TestObject + { + Id = Guid.NewGuid().ToString(), + Pk = testPartitionId, + Other = "Created by Stored Procedure" + }; + + using (ResponseMessage executeResponse = + await this.container.Scripts.ExecuteStoredProcedureStreamAsync( + sprocId, + new PartitionKey(testPartitionId), + new dynamic[] { testItem })) + { + Assert.AreEqual(HttpStatusCode.OK, executeResponse.StatusCode); + Assert.IsNotNull(executeResponse.Content); + string diagnostics = executeResponse.Diagnostics.ToString(); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + + // Delete stored procedure + await this.container.Scripts.DeleteStoredProcedureAsync(sprocId); + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task HttpRequestVersionIsTwoPointZeroWhenUsingThinClientMode() + { + Version expectedGatewayVersion = new(1, 1); + Version expectedThinClientVersion = new(2, 0); + + List postRequestVersions = new(); + + CosmosClientBuilder builder = new CosmosClientBuilder(this.connectionString) + .WithConnectionModeGateway() + .WithSendingRequestEventArgs((sender, e) => + { + if (e.HttpRequest.Method == HttpMethod.Post) + { + postRequestVersions.Add(e.HttpRequest.Version); + } + }); + + using CosmosClient client = builder.Build(); + + string dbId = "HttpVersionTestDb_" + Guid.NewGuid(); + Cosmos.Database database = await client.CreateDatabaseIfNotExistsAsync(dbId); + Container container = await database.CreateContainerIfNotExistsAsync("HttpVersionTestContainer", "/pk"); + + ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity(); + + ItemResponse response = await container.CreateItemAsync(testItem, new Cosmos.PartitionKey(testItem.pk)); + Assert.IsNotNull(response); + + Assert.AreEqual(3, postRequestVersions.Count, "Expected exactly 3 POST requests (DB, Container, Item)."); + + Assert.AreEqual(expectedGatewayVersion, postRequestVersions[0], "Expected HTTP/1.1 for CreateDatabaseAsync."); + Assert.AreEqual(expectedGatewayVersion, postRequestVersions[1], "Expected HTTP/1.1 for CreateContainerAsync."); + Assert.AreEqual(expectedThinClientVersion, postRequestVersions[2], "Expected HTTP/2.0 for CreateItemAsync."); + + await database.DeleteAsync(); + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task CreateItemsTest() + { + string pk = "pk_create"; + IEnumerable items = this.GenerateItems(pk); + + foreach (TestObject item in items) + { + ItemResponse response = await this.container.CreateItemAsync(item, new PartitionKey(item.Pk)); + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); + string diagnostics = response.Diagnostics.ToString(); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task CreateItemsTestWithThinClientFlagEnabledAndAccountDisabled() + { + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); + string authKey = Utils.ConfigurationManager.AppSettings["MasterKey"]; + string endpoint = Utils.ConfigurationManager.AppSettings["GatewayEndpoint"]; + AzureKeyCredential masterKeyCredential = new AzureKeyCredential(authKey); + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = null, + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + this.cosmosSystemTextJsonSerializer = new MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer(jsonSerializerOptions); + + this.client = new CosmosClient( + endpoint, + masterKeyCredential, + new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Gateway, + Serializer = this.cosmosSystemTextJsonSerializer, + }); + + string uniqueDbName = "TestDb2_" + Guid.NewGuid().ToString(); + this.database = await this.client.CreateDatabaseIfNotExistsAsync(uniqueDbName); + string uniqueContainerName = "TestContainer2_" + Guid.NewGuid().ToString(); + this.container = await this.database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk"); + + string pk = "pk_create"; + IEnumerable items = this.GenerateItems(pk); + + foreach (TestObject item in items) + { + ItemResponse response = await this.container.CreateItemAsync(item, new PartitionKey(item.Pk)); + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); + string diagnostics = response.Diagnostics.ToString(); + Assert.IsFalse(diagnostics.Contains("|F4"), "Diagnostics User Agent should NOT contain '|F4' for Gateway"); + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task CreateItemsTestWithDirectMode_ThinClientFlagEnabledAndAccountEnabled() + { + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = null, + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + this.cosmosSystemTextJsonSerializer = new MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer(jsonSerializerOptions); + + this.client = new CosmosClient( + this.connectionString, + new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Direct, + Serializer = this.cosmosSystemTextJsonSerializer, + }); + + string uniqueDbName = "TestDb2_" + Guid.NewGuid().ToString(); + this.database = await this.client.CreateDatabaseIfNotExistsAsync(uniqueDbName); + string uniqueContainerName = "TestContainer2_" + Guid.NewGuid().ToString(); + this.container = await this.database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk"); + + string pk = "pk_create"; + IEnumerable items = this.GenerateItems(pk); + + foreach (TestObject item in items) + { + ItemResponse response = await this.container.CreateItemAsync(item, new PartitionKey(item.Pk)); + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); + JsonDocument doc = JsonDocument.Parse(response.Diagnostics.ToString()); + string connectionMode = doc.RootElement + .GetProperty("data") + .GetProperty("Client Configuration") + .GetProperty("ConnectionMode") + .GetString(); + + Assert.AreEqual("Direct", connectionMode, "Diagnostics should have ConnectionMode set to 'Direct'"); + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task CreateItemsTestWithThinClientFlagDisabledAccountEnabled() + { + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "False"); + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = null, + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + this.cosmosSystemTextJsonSerializer = new MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer(jsonSerializerOptions); + + this.client = new CosmosClient( + this.connectionString, + new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Gateway, + Serializer = this.cosmosSystemTextJsonSerializer, + }); + + string uniqueDbName = "TestDbTCDisabled_" + Guid.NewGuid().ToString(); + this.database = await this.client.CreateDatabaseIfNotExistsAsync(uniqueDbName); + string uniqueContainerName = "TestContainerTCDisabled_" + Guid.NewGuid().ToString(); + this.container = await this.database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk"); + + string pk = "pk_create"; + IEnumerable items = this.GenerateItems(pk); + + foreach (TestObject item in items) + { + ItemResponse response = await this.container.CreateItemAsync(item, new PartitionKey(item.Pk)); + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); + string diagnostics = response.Diagnostics.ToString(); + Assert.IsFalse(diagnostics.Contains("|F4"), "Diagnostics User Agent should NOT contain '|F4' for Gateway"); + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task ReadItemsTest() + { + string pk = "pk_read"; + List items = this.GenerateItems(pk).ToList(); + + List createdItems = await this.CreateItemsSafeAsync(items); + + foreach (TestObject item in createdItems) + { + ItemResponse response = await this.container.ReadItemAsync(item.Id, new PartitionKey(item.Pk)); + string diagnostics = response.Diagnostics.ToString(); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.AreEqual(item.Id, response.Resource.Id); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task ReplaceItemsTest() + { + string pk = "pk_replace"; + List items = this.GenerateItems(pk).ToList(); + + List createdItems = await this.CreateItemsSafeAsync(items); + + foreach (TestObject item in createdItems) + { + TestObject updatedItem = new TestObject + { + Id = item.Id, + Pk = item.Pk, + Other = "Updated " + item.Other + }; + + ItemResponse response = await this.container.ReplaceItemAsync(updatedItem, updatedItem.Id, new PartitionKey(updatedItem.Pk)); + string diagnostics = response.Diagnostics.ToString(); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.AreEqual("Updated " + item.Other, response.Resource.Other); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task UpsertItemsTest() + { + string pk = "pk_upsert"; + IEnumerable items = this.GenerateItems(pk); + + foreach (TestObject item in items) + { + ItemResponse response = await this.container.UpsertItemAsync(item, new PartitionKey(item.Pk)); + string diagnostics = response.Diagnostics.ToString(); + Assert.IsTrue(response.StatusCode == HttpStatusCode.Created || response.StatusCode == HttpStatusCode.OK); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task DeleteItemsTest() + { + string pk = "pk_delete"; + List items = this.GenerateItems(pk).ToList(); + + List createdItems = await this.CreateItemsSafeAsync(items); + + foreach (TestObject item in createdItems) + { + ItemResponse response = await this.container.DeleteItemAsync(item.Id, new PartitionKey(item.Pk)); + string diagnostics = response.Diagnostics.ToString(); + Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task CreateItemStreamTest() + { + string pk = "pk_create_stream"; + IEnumerable items = this.GenerateItems(pk); + + foreach (TestObject item in items) + { + using (Stream stream = this.cosmosSystemTextJsonSerializer.ToStream(item)) + { + using (ResponseMessage response = await this.container.CreateItemStreamAsync(stream, new PartitionKey(item.Pk))) + { + string diagnostics = response.Diagnostics.ToString(); + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + } + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task ReadItemStreamTest() + { + string pk = "pk_read_stream"; + List items = this.GenerateItems(pk).ToList(); + + List createdItems = await this.CreateItemsSafeAsync(items); + + foreach (TestObject item in createdItems) + { + using (ResponseMessage response = await this.container.ReadItemStreamAsync(item.Id, new PartitionKey(item.Pk))) + { + string diagnostics = response.Diagnostics.ToString(); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task ReplaceItemStreamTest() + { + string pk = "pk_replace_stream"; + List items = this.GenerateItems(pk).ToList(); + + List createdItems = await this.CreateItemsSafeAsync(items); + + foreach (TestObject item in createdItems) + { + TestObject updatedItem = new TestObject + { + Id = item.Id, + Pk = item.Pk, + Other = "Updated " + item.Other + }; + + using (Stream stream = this.cosmosSystemTextJsonSerializer.ToStream(updatedItem)) + { + using (ResponseMessage response = await this.container.ReplaceItemStreamAsync(stream, updatedItem.Id, new PartitionKey(updatedItem.Pk))) + { + string diagnostics = response.Diagnostics.ToString(); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + } + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task UpsertItemStreamTest() + { + string pk = "pk_upsert_stream"; + IEnumerable items = this.GenerateItems(pk); + + foreach (TestObject item in items) + { + using (Stream stream = this.cosmosSystemTextJsonSerializer.ToStream(item)) + { + using (ResponseMessage response = await this.container.UpsertItemStreamAsync(stream, new PartitionKey(item.Pk))) + { + string diagnostics = response.Diagnostics.ToString(); + Assert.IsTrue(response.StatusCode == HttpStatusCode.Created || response.StatusCode == HttpStatusCode.OK); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + } + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task DeleteItemStreamTest() + { + string pk = "pk_delete_stream"; + List items = this.GenerateItems(pk).ToList(); + + List createdItems = await this.CreateItemsSafeAsync(items); + + foreach (TestObject item in createdItems) + { + using (ResponseMessage response = await this.container.DeleteItemStreamAsync(item.Id, new PartitionKey(item.Pk))) + { + string diagnostics = response.Diagnostics.ToString(); + Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode); + Assert.IsTrue(diagnostics.Contains("|F4"), "Diagnostics User Agent should contain '|F4' for ThinClient"); + } + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task QueryItemsTest() + { + string pk = "pk_query"; + List items = this.GenerateItems(pk).ToList(); + + List createdItems = await this.CreateItemsSafeAsync(items); + + string query = $"SELECT * FROM c WHERE c.pk = '{pk}'"; + FeedIterator iterator = this.container.GetItemQueryIterator(query); + + int count = 0; + while (iterator.HasMoreResults) + { + FeedResponse response = await iterator.ReadNextAsync(); + count += response.Count; + } + + Assert.AreEqual(createdItems.Count, count); + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task QueryItemsTestWithStrongConsistency() + { + string connectionString = ConfigurationManager.GetEnvironmentVariable("COSMOSDB_THINCLIENTSTRONG", string.Empty); + if (string.IsNullOrEmpty(connectionString)) + { + Assert.Fail("Set environment variable COSMOSDB_THINCLIENTSTRONG to run the tests"); + } + this.client = new CosmosClient( + connectionString, + new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Gateway, + RequestTimeout = TimeSpan.FromSeconds(60), + ConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.Strong + }); + + string uniqueDbName = "TestDbTC_" + Guid.NewGuid().ToString(); + this.database = await this.client.CreateDatabaseIfNotExistsAsync(uniqueDbName); + string uniqueContainerName = "TestContainerTC_" + Guid.NewGuid().ToString(); + this.container = await this.database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk"); + + string pk = "pk_query"; + List items = this.GenerateItems(pk).ToList(); + + List createdItems = await this.CreateItemsSafeAsync(items); + + string query = $"SELECT * FROM c WHERE c.pk = '{pk}'"; + FeedIterator iterator = this.container.GetItemQueryIterator(query); + + int count = 0; + while (iterator.HasMoreResults) + { + FeedResponse response = await iterator.ReadNextAsync(); + count += response.Count; + } + + Assert.AreEqual(createdItems.Count, count); + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task QueryItemsTestWithSessionConsistency() + { + this.client = new CosmosClient( + this.connectionString, + new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Gateway, + RequestTimeout = TimeSpan.FromSeconds(60), + ConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.Session + }); + + string uniqueDbName = "TestDbTC_" + Guid.NewGuid().ToString(); + this.database = await this.client.CreateDatabaseIfNotExistsAsync(uniqueDbName); + string uniqueContainerName = "TestContainerTC_" + Guid.NewGuid().ToString(); + this.container = await this.database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk"); + + string pk = "pk_query"; + List items = this.GenerateItems(pk).ToList(); + + List createdItems = await this.CreateItemsSafeAsync(items); + + string query = $"SELECT * FROM c WHERE c.pk = '{pk}'"; + FeedIterator iterator = this.container.GetItemQueryIterator(query); + + int count = 0; + while (iterator.HasMoreResults) + { + FeedResponse response = await iterator.ReadNextAsync(); + count += response.Count; + } + + Assert.AreEqual(createdItems.Count, count); + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task QueryItemsStreamTest() + { + string pk = "pk_query_stream"; + List items = this.GenerateItems(pk).ToList(); + + List createdItems = await this.CreateItemsSafeAsync(items); + + QueryDefinition query = new QueryDefinition("SELECT * FROM c WHERE c.pk = @pk").WithParameter("@pk", pk); + FeedIterator iterator = this.container.GetItemQueryStreamIterator(query); + + int count = 0; + while (iterator.HasMoreResults) + { + using (ResponseMessage response = await iterator.ReadNextAsync()) + { + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + + using (StreamReader reader = new StreamReader(response.Content)) + { + string json = await reader.ReadToEndAsync(); + using (JsonDocument doc = JsonDocument.Parse(json)) + { + count += doc.RootElement.GetProperty("Documents").GetArrayLength(); + } + } + } + } + + Assert.AreEqual(createdItems.Count, count); + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task BulkCreateItemsTest() + { + CosmosClient bulkClient = new CosmosClient( + this.connectionString, + new CosmosClientOptions + { + ConnectionMode = ConnectionMode.Gateway, + Serializer = this.cosmosSystemTextJsonSerializer, + AllowBulkExecution = true, + }); + + string pk = "pk_bulk"; + List items = this.GenerateItems(pk).ToList(); + List>> tasks = new List>>(); + + Container bulkContainer = bulkClient.GetContainer(this.database.Id, this.container.Id); + + foreach (TestObject item in items) + { + tasks.Add(bulkContainer.CreateItemAsync(item, new PartitionKey(item.Pk))); + } + + await Task.WhenAll(tasks); + + foreach (Task> task in tasks) + { + Assert.AreEqual(HttpStatusCode.Created, task.Result.StatusCode); + } + + bulkClient.Dispose(); + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task TransactionalBatchCreateItemsTest() + { + string pk = "pk_batch"; + List items = this.GenerateItems(pk).Take(100).ToList(); + + TransactionalBatch batch = this.container.CreateTransactionalBatch(new PartitionKey(pk)); + + foreach (TestObject item in items) + { + batch.CreateItem(item); + } + + TransactionalBatchResponse batchResponse = await batch.ExecuteAsync(); + Assert.AreEqual(HttpStatusCode.OK, batchResponse.StatusCode); + + for (int i = 0; i < items.Count; i++) + { + Assert.AreEqual(HttpStatusCode.Created, batchResponse[i].StatusCode); + } + } + + [TestMethod] + [TestCategory("ThinClient")] + public async Task RegionalFailoverWithHttpRequestException_EnsuresThinClientHeaderInRefreshRequest() + { + // Arrange + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); + + bool headerFoundInRefreshRequest = false; + int accountRefreshCount = 0; + bool hasThrown = false; + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = null, + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + CosmosSystemTextJsonSerializer serializer = new CosmosSystemTextJsonSerializer(jsonSerializerOptions); + + FaultInjectionDelegatingHandler faultHandler = new FaultInjectionDelegatingHandler( + (request) => + { + // Check for account refresh requests (GET to "/" with HTTP/1.1) + if (request.Method == HttpMethod.Get && + request.RequestUri.AbsolutePath == "/" && + request.Version == new Version(1, 1)) + { + accountRefreshCount++; + + // Only check header after we've thrown the exception + if (hasThrown) + { + if (request.Headers.TryGetValues( + ThinClientConstants.EnableThinClientEndpointDiscoveryHeaderName, + out IEnumerable headerValues)) + { + if (headerValues.Contains("True")) + { + headerFoundInRefreshRequest = true; + } + } + } + } + + // Throw HttpRequestException only ONCE on ThinClient POST requests + if (!hasThrown && + request.Method == HttpMethod.Post && + request.Version == new Version(2, 0)) + { + hasThrown = true; + throw new HttpRequestException("Simulated endpoint failure"); + } + }); + + CosmosClientBuilder builder = new CosmosClientBuilder(this.connectionString) + .WithConnectionModeGateway() + .WithCustomSerializer(serializer) + .WithHttpClientFactory(() => new HttpClient(faultHandler)); + + using CosmosClient client = builder.Build(); + + string uniqueDbName = "TestFailoverDb_" + Guid.NewGuid().ToString(); + Database database = await client.CreateDatabaseIfNotExistsAsync(uniqueDbName); + string uniqueContainerName = "TestFailoverContainer_" + Guid.NewGuid().ToString(); + Container container = await database.CreateContainerIfNotExistsAsync(uniqueContainerName, "/pk"); + + string pk = "pk_failover_test"; + TestObject testItem = this.GenerateItems(pk).First(); + + // Act - CreateItemAsync will fail once, then SDK retries and succeeds + ItemResponse response = await container.CreateItemAsync(testItem, new PartitionKey(testItem.Pk)); + + // Assert + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode, "Request should succeed after retry"); + Assert.IsTrue(hasThrown, "Exception should have been thrown once"); + Assert.IsTrue(headerFoundInRefreshRequest, "Account refresh after HttpRequestException should contain thin client header"); + + // Cleanup + await database.DeleteAsync(); + } + + /// + /// DelegatingHandler that intercepts HTTP requests and can inject faults + /// + private class FaultInjectionDelegatingHandler : DelegatingHandler + { + private readonly Action requestCallback; + + public FaultInjectionDelegatingHandler(Action requestCallback) + : base(new HttpClientHandler()) + { + this.requestCallback = requestCallback; + } + + protected override Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) + { + // Invoke callback which can inspect request or throw exceptions + this.requestCallback?.Invoke(request); + + // If no exception was thrown, proceed with the actual request + return base.SendAsync(request, cancellationToken); + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosNotFoundTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosNotFoundTests.cs index 1903a04861..6644dcdd64 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosNotFoundTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosNotFoundTests.cs @@ -97,6 +97,164 @@ public async Task ValidateQueryNotFoundResponse() await db.DeleteAsync(); } + /// + /// Validates that 404 with substatus 0 is returned when an item doesn't exist, + /// and 404 with substatus 1003 is returned when the container doesn't exist (Direct mode only). + /// This allows disambiguation between item not found vs owner resource (container/database) not found. + /// + /// This test uses streaming APIs (ReadItemStreamAsync). + /// + /// Note: Gateway mode has a known limitation where it cannot always distinguish between + /// item-not-found and container-not-found scenarios. Direct mode properly sets substatus 1003. + /// + [TestMethod] + [DataRow(true, DisplayName = "Gateway mode")] + [DataRow(false, DisplayName = "Direct mode")] + public async Task ValidateSubStatusCodeForItemNotFoundVsContainerNotFound_StreamingAPI(bool useGateway) + { + // Create a test client with the specified mode + using CosmosClient testClient = TestCommon.CreateCosmosClient(useGateway); + + // Create a test database and container + Database db = await testClient.CreateDatabaseAsync("NotFoundTest" + Guid.NewGuid().ToString()); + Container container = await db.CreateContainerAsync("NotFoundTest" + Guid.NewGuid().ToString(), "/pk", 500); + + // Test 1: Item doesn't exist in existing container - should return 404 with substatus 0 + ResponseMessage response = await container.ReadItemStreamAsync( + partitionKey: new Cosmos.PartitionKey(DoesNotExist), + id: DoesNotExist); + + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); + Assert.AreEqual(0, (int)response.Headers.SubStatusCode, + "SubStatusCode should be 0 when item doesn't exist in an existing container"); + + // Test 2: Container doesn't exist + // Direct mode: should return 404 with substatus 1003 + // Gateway mode: returns 404 with substatus 0 (known limitation) + Container nonExistentContainer = db.GetContainer(DoesNotExist); + response = await nonExistentContainer.ReadItemStreamAsync( + partitionKey: new Cosmos.PartitionKey(DoesNotExist), + id: DoesNotExist); + + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); + + if (!useGateway) + { + // Direct mode can distinguish container-not-found from item-not-found + Assert.AreEqual(1003, (int)response.Headers.SubStatusCode, + "SubStatusCode should be 1003 when container doesn't exist (owner resource not found)"); + } + else + { + // Gateway mode limitation: Cannot always distinguish, returns substatus 0 + Assert.AreEqual(0, (int)response.Headers.SubStatusCode, + "Gateway mode returns substatus 0 (known limitation - cannot distinguish container-not-found from item-not-found)"); + } + + // Test 3: Database doesn't exist + // Both Direct and Gateway mode should return 404 with substatus 1003 + // because the collection cache detects the database doesn't exist + Database nonExistentDb = testClient.GetDatabase(DoesNotExist); + Container containerInNonExistentDb = nonExistentDb.GetContainer(DoesNotExist); + response = await containerInNonExistentDb.ReadItemStreamAsync( + partitionKey: new Cosmos.PartitionKey(DoesNotExist), + id: DoesNotExist); + + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); + Assert.AreEqual(1003, (int)response.Headers.SubStatusCode, + "SubStatusCode should be 1003 when database doesn't exist (owner resource not found)"); + + // Cleanup + await db.DeleteAsync(); + } + + /// + /// Validates that CosmosException exposes the substatus code when thrown, + /// allowing developers to distinguish between different types of 404 errors. + /// + /// This test uses non-streaming/typed APIs (ReadItemAsync). + /// + /// Note: Gateway mode has a known limitation where it cannot always distinguish between + /// item-not-found and container-not-found scenarios. Direct mode properly sets substatus 1003. + /// + [TestMethod] + [DataRow(true, DisplayName = "Gateway mode")] + [DataRow(false, DisplayName = "Direct mode")] + public async Task ValidateCosmosExceptionSubStatusCodeForNotFound_TypedAPI(bool useGateway) + { + // Create a test client with the specified mode + using CosmosClient testClient = TestCommon.CreateCosmosClient(useGateway); + + // Create a test database and container + Database db = await testClient.CreateDatabaseAsync("NotFoundTest" + Guid.NewGuid().ToString()); + Container container = await db.CreateContainerAsync("NotFoundTest" + Guid.NewGuid().ToString(), "/pk", 500); + + // Test 1: Item doesn't exist in existing container - CosmosException should have substatus 0 + try + { + ItemResponse response = await container.ReadItemAsync( + partitionKey: new Cosmos.PartitionKey(DoesNotExist), + id: DoesNotExist); + Assert.Fail("Expected CosmosException to be thrown"); + } + catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + Assert.AreEqual(0, ex.SubStatusCode, + "SubStatusCode should be 0 when item doesn't exist in an existing container"); + } + + // Test 2: Container doesn't exist + // Direct mode: CosmosException should have substatus 1003 + // Gateway mode: CosmosException has substatus 0 (known limitation) + Container nonExistentContainer = db.GetContainer(DoesNotExist); + try + { + ItemResponse response = await nonExistentContainer.ReadItemAsync( + partitionKey: new Cosmos.PartitionKey(DoesNotExist), + id: DoesNotExist); + Assert.Fail("Expected CosmosException to be thrown"); + } + catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + if (!useGateway) + { + // Direct mode can distinguish container-not-found from item-not-found + Assert.AreEqual(1003, ex.SubStatusCode, + "SubStatusCode should be 1003 when container doesn't exist (owner resource not found)"); + } + else + { + // Gateway mode limitation: Cannot always distinguish, returns substatus 0 + Assert.AreEqual(0, ex.SubStatusCode, + "Gateway mode returns substatus 0 (known limitation - cannot distinguish container-not-found from item-not-found)"); + } + } + + // Test 3: Database doesn't exist + // Both Direct and Gateway mode should return substatus 1003 + // because the collection cache detects the database doesn't exist + Database nonExistentDb = testClient.GetDatabase(DoesNotExist); + Container containerInNonExistentDb = nonExistentDb.GetContainer(DoesNotExist); + try + { + ItemResponse response = await containerInNonExistentDb.ReadItemAsync( + partitionKey: new Cosmos.PartitionKey(DoesNotExist), + id: DoesNotExist); + Assert.Fail("Expected CosmosException to be thrown"); + } + catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + Assert.AreEqual(1003, ex.SubStatusCode, + "SubStatusCode should be 1003 when database doesn't exist (owner resource not found)"); + } + + // Cleanup + await db.DeleteAsync(); + } + private async Task ContainerOperations(Database database, bool dbNotExist) { // Create should fail if the database does not exist @@ -162,6 +320,52 @@ private async Task ItemOperations(Container container, bool containerNotExist) streamPayload: replace)); } + /// + /// Validates that container metadata operations (ReadContainer, DeleteContainer) return 404 with substatus 0 + /// when the container doesn't exist, as opposed to item operations which return substatus 1003. + /// This ensures we don't incorrectly set substatus 1003 for container-level operations. + /// + [TestMethod] + [DataRow(true, DisplayName = "Gateway mode")] + [DataRow(false, DisplayName = "Direct mode")] + public async Task ValidateContainerMetadataRequestsHaveSubstatus0(bool useGateway) + { + // Create a test client with the specified mode + using CosmosClient testClient = TestCommon.CreateCosmosClient(useGateway); + + // Create a test database + Database db = await testClient.CreateDatabaseAsync("NotFoundTest" + Guid.NewGuid().ToString()); + + // Test 1: ReadContainer on non-existent container should return 404 with substatus 0 + Container nonExistentContainer = db.GetContainer(DoesNotExist); + ResponseMessage response = await nonExistentContainer.ReadContainerStreamAsync(); + + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); + Assert.AreEqual(0, (int)response.Headers.SubStatusCode, + "ReadContainer should return substatus 0 when container doesn't exist (the container itself is the missing resource)"); + + // Test 2: DeleteContainer on non-existent container should also return 404 with substatus 0 + response = await nonExistentContainer.DeleteContainerStreamAsync(); + + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); + Assert.AreEqual(0, (int)response.Headers.SubStatusCode, + "DeleteContainer should return substatus 0 when container doesn't exist (the container itself is the missing resource)"); + + // Test 3: ReplaceContainer on non-existent container should also return 404 with substatus 0 + ContainerProperties containerSettings = new ContainerProperties(id: DoesNotExist, partitionKeyPath: "/pk"); + response = await nonExistentContainer.ReplaceContainerStreamAsync(containerSettings); + + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); + Assert.AreEqual(0, (int)response.Headers.SubStatusCode, + "ReplaceContainer should return substatus 0 when container doesn't exist (the container itself is the missing resource)"); + + // Cleanup + await db.DeleteAsync(); + } + private async Task VerifyQueryNotFoundResponse(FeedIterator iterator) { ResponseMessage response = await iterator.ReadNextAsync(); @@ -175,4 +379,4 @@ private void VerifyNotFoundResponse(ResponseMessage response) Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); } } -} \ No newline at end of file +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosOperationCanceledExceptionTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosOperationCanceledExceptionTests.cs index 52c1aab8b0..4c8735d73b 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosOperationCanceledExceptionTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosOperationCanceledExceptionTests.cs @@ -7,6 +7,8 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests using System; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Diagnostics; + using Microsoft.Azure.Cosmos.Tracing; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -76,6 +78,32 @@ public async Task CheckCancellationTokenDirectTestAsync() await this.CheckCancellationTokenTestAsync(this.Container, cancellationTokenSource.Token); } + [TestMethod] + public void CheckToJsonStringConversion() + { + // This test is reproducing an issue that an internal customer faced after enabling + // cross region hedging + // In their scenario they occassionally hit e2e timeouts as CosmosOperationCancelledException + // The JSON transformation of the CosmosOperationCancelledException in this case could run into + // an issue (InvalidOperationException due to Lazy factory hitting a cyclic dependency + // when neither the diagnostics nor the exception had materialized the json string yet + // because the CosmosOperationCancelledException ias added as a trace datum to the ITrace of + // the CosmosTraceDiagnostics instance. + // The test below reproduced the issue - and is kept here to validate the fix and + // as a regression test + using ITrace outerTrace = Trace.GetRootTrace("cyclicDependencyReproOuter"); + using ITrace innerTrace = outerTrace.StartChild("cyclicDependencyReproInner"); + + OperationCanceledException innerTimeout = new OperationCanceledException(); + CosmosOperationCanceledException innerCosmosTimeoutException = new CosmosOperationCanceledException( + innerTimeout, + innerTrace); + CosmosOperationCanceledException outerCosmosTimeoutException = new CosmosOperationCanceledException( + innerCosmosTimeoutException, + outerTrace); + + Console.WriteLine(outerCosmosTimeoutException.ToString()); + } private async Task CheckCancellationTokenTestAsync( Container container, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DistributedTransaction/DistributedTransactionE2ETests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DistributedTransaction/DistributedTransactionE2ETests.cs new file mode 100644 index 0000000000..4e44e622cd --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DistributedTransaction/DistributedTransactionE2ETests.cs @@ -0,0 +1,293 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Text; + using System.Text.Json; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using OperationType = Documents.OperationType; + + [TestClass] + public class DistributedTransactionE2ETests : BaseCosmosClientHelper + { + private const string IdempotencyTokenHeader = "x-ms-dtc-operation-id"; + private const string PartitionKeyPath = "/pk"; + + private Container container; + + [TestInitialize] + public async Task TestInitialize() + { + await this.TestInit(); + + ContainerResponse response = await this.database.CreateContainerAsync( + new ContainerProperties(id: Guid.NewGuid().ToString(), partitionKeyPath: PartitionKeyPath), + cancellationToken: this.cancellationToken); + + this.container = response.Container; + } + + [TestCleanup] + public async Task Cleanup() + { + await base.TestCleanup(); + } + + [TestMethod] + public async Task ValidateHappyPathRequestAndResponse() + { + // Arrange + ToDoActivity doc1 = ToDoActivity.CreateRandomToDoActivity(); + ToDoActivity doc2 = ToDoActivity.CreateRandomToDoActivity(); + + DistributedTransactionTestHandler handler = CreateMockHandler( + HttpStatusCode.OK, + CreateMockSuccessResponse(operationCount: 2)); + + using CosmosClient client = TestCommon.CreateCosmosClient( + clientOptions: new CosmosClientOptions + { + CustomHandlers = { handler }, + ConnectionMode = ConnectionMode.Gateway + }); + + // Act + DistributedTransactionResponse response = await client.CreateDistributedWriteTransaction() + .CreateItem(this.database.Id, this.container.Id, new PartitionKey(doc1.pk), doc1) + .CreateItem(this.database.Id, this.container.Id, new PartitionKey(doc2.pk), doc2) + .CommitTransactionAsync(CancellationToken.None); + + // Assert - Request + Assert.IsNotNull(handler.CapturedRequest); + Assert.IsNotNull(handler.CapturedRequest.Headers[IdempotencyTokenHeader]); + ValidateRequestBody(handler.CapturedRequestBody, doc1, doc2); + + // Assert - Response + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.IsTrue(response.IsSuccessStatusCode); + Assert.AreEqual(2, response.Count); + + response.Dispose(); + } + + [TestMethod] + public async Task ValidateMixedOperationsRequestStructure() + { + // Arrange + ToDoActivity createDoc = ToDoActivity.CreateRandomToDoActivity(); + ToDoActivity replaceDoc = ToDoActivity.CreateRandomToDoActivity(); + + DistributedTransactionTestHandler handler = CreateMockHandler( + HttpStatusCode.OK, + CreateMockSuccessResponse(operationCount: 3)); + + using CosmosClient client = TestCommon.CreateCosmosClient( + clientOptions: new CosmosClientOptions + { + CustomHandlers = { handler }, + ConnectionMode = ConnectionMode.Gateway + }); + + // Act + DistributedTransactionResponse response = await client.CreateDistributedWriteTransaction() + .CreateItem(this.database.Id, this.container.Id, new PartitionKey(createDoc.pk), createDoc) + .ReplaceItem(this.database.Id, this.container.Id, new PartitionKey(replaceDoc.pk), replaceDoc.id, replaceDoc) + .DeleteItem(this.database.Id, this.container.Id, new PartitionKey("delete-pk"), "delete-id") + .CommitTransactionAsync(CancellationToken.None); + + // Assert + using JsonDocument requestJson = JsonDocument.Parse(handler.CapturedRequestBody); + JsonElement operations = requestJson.RootElement.GetProperty("operations"); + + Assert.AreEqual(3, operations.GetArrayLength()); + Assert.AreEqual((int)OperationType.Create, operations[0].GetProperty("operationType").GetInt32()); // Create + Assert.AreEqual((int)OperationType.Replace, operations[1].GetProperty("operationType").GetInt32()); // Replace + Assert.AreEqual((int)OperationType.Delete, operations[2].GetProperty("operationType").GetInt32()); // Delete + + response.Dispose(); + } + + [TestMethod] + public async Task ValidateConflictResponseReturnsErrorStatus() + { + // Arrange + string mockErrorResponse = @"{ + ""operationResponses"": [{ + ""index"": 0, + ""statuscode"": 409, + ""substatuscode"": 0 + }] + }"; + + DistributedTransactionTestHandler handler = CreateMockHandler(HttpStatusCode.Conflict, mockErrorResponse); + using CosmosClient client = TestCommon.CreateCosmosClient( + clientOptions: new CosmosClientOptions + { + CustomHandlers = { handler }, + ConnectionMode = ConnectionMode.Gateway + }); + + ToDoActivity doc = ToDoActivity.CreateRandomToDoActivity(); + + // Act + DistributedTransactionResponse response = await client.CreateDistributedWriteTransaction() + .CreateItem(this.database.Id, this.container.Id, new PartitionKey(doc.pk), doc) + .CommitTransactionAsync(CancellationToken.None); + + // Assert + Assert.AreEqual(HttpStatusCode.Conflict, response.StatusCode); + Assert.IsFalse(response.IsSuccessStatusCode); + Assert.AreEqual(1, response.Count); + Assert.AreEqual(HttpStatusCode.Conflict, response[0].StatusCode); + + response.Dispose(); + } + + [TestMethod] + public async Task ValidateResponseDeserializesCorrectly() + { + // Arrange + ToDoActivity expectedDoc = ToDoActivity.CreateRandomToDoActivity(); + string base64Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(expectedDoc))); + + string mockResponse = $@"{{ + ""operationResponses"": [{{ + ""index"": 0, + ""statuscode"": 201, + ""etag"": ""\""test-etag\"""", + ""resourcebody"": ""{base64Body}"" + }}] + }}"; + + DistributedTransactionTestHandler handler = CreateMockHandler(HttpStatusCode.OK, mockResponse); + using CosmosClient client = TestCommon.CreateCosmosClient( + clientOptions: new CosmosClientOptions + { + CustomHandlers = { handler }, + ConnectionMode = ConnectionMode.Gateway + }); + + // Act + DistributedTransactionResponse response = await client.CreateDistributedWriteTransaction() + .CreateItem(this.database.Id, this.container.Id, new PartitionKey(expectedDoc.pk), expectedDoc) + .CommitTransactionAsync(CancellationToken.None); + + // Assert + Assert.AreEqual(HttpStatusCode.Created, response[0].StatusCode); + Assert.AreEqual("\"test-etag\"", response[0].ETag); + Assert.IsNotNull(response[0].ResourceStream); + + using StreamReader reader = new StreamReader(response[0].ResourceStream); + ToDoActivity returnedDoc = JsonSerializer.Deserialize(await reader.ReadToEndAsync()); + + Assert.AreEqual(expectedDoc.id, returnedDoc.id); + Assert.AreEqual(expectedDoc.pk, returnedDoc.pk); + + response.Dispose(); + } + + #region Helper Methods + + private static DistributedTransactionTestHandler CreateMockHandler(HttpStatusCode statusCode, string responseBody) + { + return new DistributedTransactionTestHandler + { + MockResponseFactory = request => + { + ResponseMessage response = new ResponseMessage(statusCode, request, errorMessage: null) + { + Content = new MemoryStream(Encoding.UTF8.GetBytes(responseBody)) + }; + response.Headers["x-ms-activity-id"] = Guid.NewGuid().ToString(); + response.Headers[IdempotencyTokenHeader] = request.Headers[IdempotencyTokenHeader] ?? Guid.NewGuid().ToString(); + return Task.FromResult(response); + } + }; + } + + private static string CreateMockSuccessResponse(int operationCount) + { + List responses = new(); + for (int i = 0; i < operationCount; i++) + { + responses.Add($@"{{""index"":{i},""statusCode"":201,""etag"":""\""etag-{i}\""""}}"); + } + return $@"{{""operationResponses"":[{string.Join(",", responses)}]}}"; + } + + private static void ValidateRequestBody(string requestBody, params ToDoActivity[] expectedDocs) + { + using JsonDocument json = JsonDocument.Parse(requestBody); + JsonElement operations = json.RootElement.GetProperty("operations"); + + Assert.AreEqual(expectedDocs.Length, operations.GetArrayLength()); + + for (int i = 0; i < expectedDocs.Length; i++) + { + JsonElement op = operations[i]; + + Assert.AreEqual(i, op.GetProperty("index").GetInt32()); + Assert.IsTrue(op.TryGetProperty("databaseName", out _)); + Assert.IsTrue(op.TryGetProperty("collectionName", out _)); + Assert.IsTrue(op.TryGetProperty("operationType", out _)); + + // resourceBody is now a nested JSON object, not a string + JsonElement resourceBody = op.GetProperty("resourceBody"); + Assert.AreEqual(JsonValueKind.Object, resourceBody.ValueKind); + + ToDoActivity actualDoc = JsonSerializer.Deserialize(resourceBody.GetRawText()); + ToDoActivity expectedDoc = expectedDocs[i]; + + Assert.AreEqual(expectedDoc.id, actualDoc.id); + Assert.AreEqual(expectedDoc.pk, actualDoc.pk); + Assert.AreEqual(expectedDoc.taskNum, actualDoc.taskNum); + Assert.AreEqual(expectedDoc.cost, actualDoc.cost); + Assert.AreEqual(expectedDoc.description, actualDoc.description); + } + } + + #endregion + + #region Test Handler + + private class DistributedTransactionTestHandler : RequestHandler + { + public RequestMessage CapturedRequest { get; private set; } + public string CapturedRequestBody { get; private set; } + public Func> MockResponseFactory { get; set; } + + public override async Task SendAsync(RequestMessage request, CancellationToken cancellationToken) + { + if (request.RequestUriString?.StartsWith("/dtc/", StringComparison.OrdinalIgnoreCase) == true) + { + this.CapturedRequest = request; + + if (request.Content != null) + { + using MemoryStream ms = new(); + await request.Content.CopyToAsync(ms); + this.CapturedRequestBody = Encoding.UTF8.GetString(ms.ToArray()); + request.Content.Position = 0; + } + + return this.MockResponseFactory != null + ? await this.MockResponseFactory(request) + : new ResponseMessage(HttpStatusCode.OK, request, errorMessage: null); + } + + return await base.SendAsync(request, cancellationToken); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DocumentClientUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DocumentClientUnitTests.cs index 43fe495852..34a35ccd46 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DocumentClientUnitTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DocumentClientUnitTests.cs @@ -8,14 +8,18 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests using System.Globalization; using System.Net; using System.Net.Http; + using System.Net.Security; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Common; using Microsoft.Azure.Cosmos.Internal; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; + using Microsoft.Azure.Cosmos.Tests; using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Cosmos.Utils; using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Client; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -272,6 +276,112 @@ private void TestRetryOnThrottled(int? numberOfRetries) Assert.IsTrue(throttled); } + [TestMethod] + [DataRow(false, DisplayName = "NRegion Synchronous commit is disabled for the account")] + [DataRow(true, DisplayName = "NRegion Synchronous commit is enabled for the account")] + public void EnableNRegionSynchronousCommit_PassedToStoreClient(bool nRegionCommitEnabled) + { + + StoreClient storeClient = new StoreClient( + new Mock().Object, + new SessionContainer(string.Empty), + new Mock().Object, + new Mock().Object, + Protocol.Tcp, + new Mock().Object); + // Arrange + Mock mockStoreClientFactory = new Mock(); + mockStoreClientFactory.Setup(f => f.CreateStoreClient( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny() + )).Returns(storeClient); + + DocumentClient documentClient = new DocumentClient( + new Uri("https://localhost:8081"), + new Mock().Object, + new EventHandler((s, e) => { }), + new ConnectionPolicy(), + null, // desiredConsistencyLevel + null, // serializerSettings + ApiType.None, + new EventHandler((s, e) => { }), + null, // handler + new Mock().Object, + null, // enableCpuMonitor + new Func(tc => tc), + mockStoreClientFactory.Object, + false, // isLocalQuorumConsistency + "testClientId", + new RemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) => true), + new Mock().Object, + new Mock().Object, + true // enableAsyncCacheExceptionNoSharing + ); + + AccountProperties accountProperties = new AccountProperties + { + // Set the property to true for test + EnableNRegionSynchronousCommit = nRegionCommitEnabled, + }; + + AccountConsistency ac = new AccountConsistency(); + ac.DefaultConsistencyLevel = (Cosmos.ConsistencyLevel) ConsistencyLevel.Session; + accountProperties.Consistency = ac; + + Func> getDatabaseAccountFn = () => + // When called with any Uri, return the expected AccountProperties + Task.FromResult(accountProperties); + + CosmosAccountServiceConfiguration accountServiceConfiguration = new CosmosAccountServiceConfiguration( + getDatabaseAccountFn); + + typeof(CosmosAccountServiceConfiguration) + .GetProperty("AccountProperties", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) + .SetValue(accountServiceConfiguration, accountProperties); + + //Inject the accountServiceConfiguration into the DocumentClient via reflection. + typeof(DocumentClient) + .GetProperty("accountServiceConfiguration", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) + .SetValue(documentClient, accountServiceConfiguration); + + + typeof(DocumentClient) + .GetField("storeClientFactory", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) + .SetValue(documentClient, mockStoreClientFactory.Object); + + // Act: Call the private method via reflection + typeof(DocumentClient) + .GetMethod("CreateStoreModel", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) + .Invoke(documentClient, new object[] { true }); + + // Assert: Verify the correct value was passed + mockStoreClientFactory.Verify(f => + f.CreateStoreClient( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.Is(config => config.EnableNRegionSynchronousCommit == accountProperties.EnableNRegionSynchronousCommit), + It.IsAny()), + Times.Once, + "EnableNRegionSynchronousCommit was not passed correctly to AccountConfigurationProperties and StoreClient."); + } private DocumentClientException CreateTooManyRequestException(int retryAfterInMilliseconds) { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/ChangeFeedIteratorCoreTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/ChangeFeedIteratorCoreTests.cs index a43d86faf5..ca679ab6a8 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/ChangeFeedIteratorCoreTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/ChangeFeedIteratorCoreTests.cs @@ -896,6 +896,7 @@ private async Task ValidateChangeFeedIteratorCore_WithQuery( foreach (ChangeFeedItem item in feedResponse) { + Assert.AreEqual(expected: "id3", actual: item.Metadata.Id.ToString()); Assert.AreEqual("id3", item.Previous.Id); Assert.AreEqual(ChangeFeedOperationType.Delete, item.Metadata.OperationType); } @@ -1094,6 +1095,7 @@ public async Task ChangeFeedIteratorCore_FeedRange_VerifyingWireFormatTests() Assert.AreNotEqual(notExpected: default, actual: deleteOperation.Metadata.Lsn); Assert.AreNotEqual(notExpected: default, actual: deleteOperation.Metadata.PreviousLsn); Assert.IsNotNull(deleteOperation.Previous); + Assert.AreEqual(expected: id, actual: deleteOperation.Metadata.Id.ToString()); Assert.AreEqual(expected: id, actual: deleteOperation.Previous.Id); Assert.AreEqual(expected: "205 16th St NW", actual: deleteOperation.Previous.Line1); Assert.AreEqual(expected: "Atlanta", actual: deleteOperation.Previous.City); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/QueryFeedTokenTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/QueryFeedTokenTests.cs index 74a8cf6f7c..2bf1d9ea4b 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/QueryFeedTokenTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/QueryFeedTokenTests.cs @@ -57,7 +57,7 @@ public async Task GetTargetPartitionKeyRangesAsyncWithFeedRange() effectivePartitionKeyRanges: null, containerResponse.Resource.PartitionKey, vectorEmbeddingPolicy: null, - containerResponse.Resource.GeospatialConfig.GeospatialType); + containerResponse.Resource.GeospatialConfig.GeospatialType, false); IReadOnlyList feedTokens = await container.GetFeedRangesAsync(); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Fluent/ContainerSettingsTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Fluent/ContainerSettingsTests.cs index 16186a5835..e48b2f440c 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Fluent/ContainerSettingsTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Fluent/ContainerSettingsTests.cs @@ -3,7 +3,7 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests -{ +{ using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json.Linq; using System; @@ -1304,8 +1304,104 @@ public async Task WithClientEncryptionPolicyFailureTest() { Assert.IsTrue(ex.Message.Contains("Only Deterministic encryption type is supported for path: /id."), ex.Message); } - } - + } + + [Ignore("Marking as ignore until emulator is updated")] + [TestMethod] + [DataRow("en-US")] + [DataRow("fr-FR")] + [DataRow("de-DE")] + [DataRow("it-IT")] + [DataRow("pt-BR")] + [DataRow("pt-PT")] + [DataRow("es-ES")] + public async Task TestFullTextSearchPolicyWithAllSupportedDefaultLanguages(string defaultLanguage) + { + string fullTextPath1 = "/fts1", fullTextPath2 = "/fts2"; + string endpoint = ""; + string key = ""; + + string databaseName = "TestDatabaseFullTextPolicy"; + string containerName = "TestContainerFullTextPolicy_"+ defaultLanguage; + + CosmosClientOptions clientOptions = new CosmosClientOptions + { + ConnectionMode = ConnectionMode.Direct, + }; + CosmosClient client = new(endpoint, key, clientOptions); + + Database databaseForFullTextSearch = await client.CreateDatabaseIfNotExistsAsync(databaseName); + try + { + string partitionKeyPath = "/pk"; + + Collection fullTextPaths = new Collection() + { + new FullTextPath() + { + Path = fullTextPath1, + Language = defaultLanguage, + }, + new FullTextPath() + { + Path = fullTextPath2, + Language = defaultLanguage, + } + }; + + ContainerResponse containerResponse = + await databaseForFullTextSearch.DefineContainer(containerName, partitionKeyPath) + .WithFullTextPolicy( + defaultLanguage: defaultLanguage, + fullTextPaths: fullTextPaths) + .Attach() + .WithIndexingPolicy() + .WithFullTextIndex() + .Path(fullTextPath1) + .Attach() + .WithFullTextIndex() + .Path(fullTextPath2) + .Attach() + .Attach() + .CreateAsync(); + + Assert.AreEqual(HttpStatusCode.Created, containerResponse.StatusCode, + $"Failed to create container with default language: {defaultLanguage}"); + Assert.AreEqual(containerName, containerResponse.Resource.Id); + Assert.AreEqual(partitionKeyPath, containerResponse.Resource.PartitionKey.Paths.First()); + + ContainerProperties containerSettings = containerResponse.Resource; + + // Validate FullText Policy + Assert.IsNotNull(containerSettings.FullTextPolicy, + $"FullTextPolicy is null for language: {defaultLanguage}"); + Assert.AreEqual(defaultLanguage, containerSettings.FullTextPolicy.DefaultLanguage, + $"DefaultLanguage mismatch for: {defaultLanguage}"); + Assert.IsNotNull(containerSettings.FullTextPolicy.FullTextPaths); + Assert.AreEqual(fullTextPaths.Count, containerSettings.FullTextPolicy.FullTextPaths.Count()); + + // Validate each path has the correct language + foreach (FullTextPath path in containerSettings.FullTextPolicy.FullTextPaths) + { + Assert.AreEqual(defaultLanguage, path.Language, + $"Path language mismatch for default language: {defaultLanguage}"); + } + + // Validate Full Text Indexes + Assert.IsNotNull(containerSettings.IndexingPolicy.FullTextIndexes); + Assert.AreEqual(fullTextPaths.Count, containerSettings.IndexingPolicy.FullTextIndexes.Count()); + Assert.AreEqual(fullTextPath1, containerSettings.IndexingPolicy.FullTextIndexes[0].Path); + Assert.AreEqual(fullTextPath2, containerSettings.IndexingPolicy.FullTextIndexes[1].Path); + + // Clean up container after test + await containerResponse.Container.DeleteContainerAsync(); + } + finally + { + await databaseForFullTextSearch.DeleteAsync(); + } + } + private bool VerifyClientEncryptionIncludedPath(ClientEncryptionIncludedPath expected, ClientEncryptionIncludedPath actual) { return expected.Path == actual.Path && diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/GatewayTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/GatewayTests.cs index 41ccbc2da4..50e7f78484 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/GatewayTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/GatewayTests.cs @@ -2275,7 +2275,7 @@ public async Task ValidateStoredProceduresBlacklistingInternal() { PartitionKey = new Documents.PartitionKey("test") }; - for (int numExec = 0; numExec < 3; numExec++) + for (int numExec = 0; numExec < 6; numExec++) { client.ExecuteStoredProcedureAsync(retrievedStoredProcedure, requestOptions).Wait(); } @@ -2472,7 +2472,7 @@ public async Task ValidateUserDefinedFunctionsBlacklistingInternal() // create one doc and try again await client.CreateDocumentAsync(collection, new Document() { Id = "newdoc1" }); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 6; i++) { IDocumentQuery docQuery2 = secondaryClient.CreateDocumentQuery(collection.DocumentsLink, "select udf.badUdf(r.id) from root r", new FeedOptions { EnableCrossPartitionQuery = true }).AsDocumentQuery(); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemLinqTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/CosmosItemLinqTests.cs similarity index 96% rename from Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemLinqTests.cs rename to Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/CosmosItemLinqTests.cs index 9f1b8c2177..846739cf98 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemLinqTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/CosmosItemLinqTests.cs @@ -313,7 +313,7 @@ public async Task QueryableExtentionFunctionsTest() //Creating items for query. IList itemList = await ToDoActivity.CreateRandomItems(container: this.Container, pkCount: 10, perPKItemCount: 1, randomPartitionKey: true); - QueryRequestOptions queryRequestOptions = new QueryRequestOptions(); + QueryRequestOptions queryRequestOptions = new QueryRequestOptions() { PopulateIndexMetrics = true }; IOrderedQueryable linqQueryable = this.Container.GetItemLinqQueryable( requestOptions: queryRequestOptions); @@ -396,6 +396,34 @@ public async Task QueryableExtentionFunctionsTest() Assert.AreEqual(100, maxTaskNum); } + + [TestMethod] + public async Task GetIndexMetricsTest() + { + //Creating items for query. + IList itemList = await ToDoActivity.CreateRandomItems(container: this.Container, pkCount: 10, perPKItemCount: 1, randomPartitionKey: true); + + QueryRequestOptions queryRequestOptions = new QueryRequestOptions() { PopulateIndexMetrics = true }; + IOrderedQueryable linqQueryable = this.Container.GetItemLinqQueryable( + requestOptions: queryRequestOptions); + + // Response object with valid index metrics field + Response response = await linqQueryable.Select(item => item.taskNum).SumAsync(); + this.VerifyResponse(response, 420, queryRequestOptions); + + string indexMetrics = response.GetIndexMetrics(); + Assert.AreEqual( + @"{""UtilizedIndexes"":{""SingleIndexes"":[{""IndexSpec"":""/taskNum/?""}],""CompositeIndexes"":[]},""PotentialIndexes"":{""SingleIndexes"":[],""CompositeIndexes"":[]}}", + indexMetrics); + + // Response object with null index metrics field + response.Headers.IndexUtilizationText = null; + indexMetrics = response.GetIndexMetrics(); + Assert.AreEqual( + null, + indexMetrics); + } + [DataRow(false)] [DataRow(true)] [TestMethod] @@ -1012,6 +1040,9 @@ private void VerifyResponse( { Assert.AreEqual(expectedValue, response.Resource); Assert.IsTrue(response.RequestCharge > 0); + Assert.IsNotNull(response.Headers.IndexUtilizationText); + Assert.IsNotNull(response.Headers.ActivityId); + Assert.IsNotNull(response.ActivityId); } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqTestsCommon.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqTestsCommon.cs index 37d2b62e6a..8dae40100c 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqTestsCommon.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqTestsCommon.cs @@ -1,41 +1,41 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- -namespace Microsoft.Azure.Cosmos.Services.Management.Tests -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Diagnostics; - using System.IO; - using System.Linq; - using System.Linq.Expressions; - using System.Reflection; - using System.Runtime.CompilerServices; - using System.Text; - using System.Text.Json.Serialization; - using System.Text.Json; - using System.Text.RegularExpressions; - using System.Xml; - using global::Azure.Core.Serialization; - using Microsoft.Azure.Cosmos.Services.Management.Tests.BaselineTest; - using Microsoft.Azure.Documents; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using Newtonsoft.Json; +//----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//----------------------------------------------------------------------- +namespace Microsoft.Azure.Cosmos.Services.Management.Tests +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + using System.Runtime.CompilerServices; + using System.Text; + using System.Text.Json.Serialization; + using System.Text.Json; + using System.Text.RegularExpressions; + using System.Xml; + using global::Azure.Core.Serialization; + using Microsoft.Azure.Cosmos.Services.Management.Tests.BaselineTest; + using Microsoft.Azure.Documents; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json; using Newtonsoft.Json.Linq; - internal class LinqTestsCommon - { - /// - /// Compare two list of anonymous objects - /// - /// - /// - /// - private static bool CompareListOfAnonymousType(List queryResults, List dataResults, bool ignoreOrder) + internal class LinqTestsCommon + { + /// + /// Compare two list of anonymous objects + /// + /// + /// + /// + private static bool CompareListOfAnonymousType(List queryResults, List dataResults, bool ignoreOrder) { if (!ignoreOrder) { @@ -48,852 +48,852 @@ private static bool CompareListOfAnonymousType(List queryResults, List a.Equals(obj))) - { - resultMatched = false; - return false; - } - } - - foreach (dynamic obj in dataResults) - { - if (!queryResults.Any(a => a.Equals(obj))) - { - resultMatched = false; - break; - } - } - - return resultMatched; - } - - /// - /// Compare 2 IEnumerable which may contain IEnumerable themselves. - /// - /// The query results from Cosmos DB - /// The query results from actual data - /// True if the two IEbumerable equal - private static bool NestedListsSequenceEqual(IEnumerable queryResults, IEnumerable dataResults) - { - IEnumerator queryIter, dataIter; - for (queryIter = queryResults.GetEnumerator(), dataIter = dataResults.GetEnumerator(); - queryIter.MoveNext() && dataIter.MoveNext();) - { - IEnumerable queryEnumerable = queryIter.Current as IEnumerable; - IEnumerable dataEnumerable = dataIter.Current as IEnumerable; - if (queryEnumerable == null && dataEnumerable == null) - { - if (!queryIter.Current.Equals(dataIter.Current)) return false; - - } - - else if (queryEnumerable == null || dataEnumerable == null) - { - return false; - } - - else - { - if (!LinqTestsCommon.NestedListsSequenceEqual(queryEnumerable, dataEnumerable)) return false; - } - } - - return !(queryIter.MoveNext() || dataIter.MoveNext()); - } - - /// - /// Compare the list of results from CosmosDB query and the list of results from LinQ query on the original data - /// Similar to Collections.SequenceEqual with the assumption that these lists are non-empty - /// - /// A list representing the query restuls from CosmosDB - /// A list representing the linQ query results from the original data - /// true if the two - private static bool CompareListOfArrays(List queryResults, List dataResults) - { - if (NestedListsSequenceEqual(queryResults, dataResults)) return true; - - bool resultMatched = true; - - // dataResults contains type ConcatIterator whereas queryResults may contain IEnumerable - // therefore it's simpler to just cast them into List> manually for simplify the verification - List> l1 = new List>(); - foreach (IEnumerable list in dataResults) - { - List l = new List(); - IEnumerator iterator = list.GetEnumerator(); - while (iterator.MoveNext()) - { - l.Add(iterator.Current); - } - - l1.Add(l); - } - - List> l2 = new List>(); - foreach (IEnumerable list in queryResults) - { - List l = new List(); - IEnumerator iterator = list.GetEnumerator(); - while (iterator.MoveNext()) - { - l.Add(iterator.Current); - } - - l2.Add(l); - } - - foreach (IEnumerable list in l1) - { - if (!l2.Any(a => a.SequenceEqual(list))) - { - resultMatched = false; - return false; - } - } - - foreach (IEnumerable list in l2) - { - if (!l1.Any(a => a.SequenceEqual(list))) - { - resultMatched = false; - break; - } - } - - return resultMatched; - } - - private static bool IsNumber(dynamic value) - { - return value is sbyte - || value is byte - || value is short - || value is ushort - || value is int - || value is uint - || value is long - || value is ulong - || value is float - || value is double - || value is decimal; - } - - public static Boolean IsAnonymousType(Type type) - { - Boolean hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0; - Boolean nameContainsAnonymousType = type.FullName.Contains("AnonymousType"); - Boolean isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType; - - return isAnonymousType; - } - - /// - /// Gets the results of CosmosDB query and the results of LINQ query on the original data - /// - /// - /// - public static (List queryResults, List dataResults) GetResults(IQueryable queryResults, IQueryable dataResults) - { - // execution validation - IEnumerator queryEnumerator = queryResults.GetEnumerator(); - List queryResultsList = new List(); - while (queryEnumerator.MoveNext()) - { - queryResultsList.Add(queryEnumerator.Current); - } - - List dataResultsList = dataResults?.Cast()?.ToList(); - - return (queryResultsList, dataResultsList); - } - - /// - /// Validates the results of CosmosDB query and the results of LINQ query on the original data - /// Using Assert, will fail the unit test if the two results list are not SequenceEqual - /// - /// - /// - private static void ValidateResults(List queryResultsList, List dataResultsList, bool ignoreOrder) - { - bool resultMatched = true; - string actualStr = null; - string expectedStr = null; - if (dataResultsList.Count == 0 || queryResultsList.Count == 0) - { - resultMatched &= dataResultsList.Count == queryResultsList.Count; - } - else - { - dynamic firstElem = dataResultsList.FirstOrDefault(); - if (firstElem is IEnumerable) - { - resultMatched &= CompareListOfArrays(queryResultsList, dataResultsList); - } - else if (LinqTestsCommon.IsAnonymousType(firstElem.GetType())) - { - resultMatched &= CompareListOfAnonymousType(queryResultsList, dataResultsList, ignoreOrder); - } - else if (LinqTestsCommon.IsNumber(firstElem)) - { - const double Epsilon = 1E-6; - Type dataType = firstElem.GetType(); - List dataSortedList = dataResultsList.OrderBy(x => x).ToList(); - List querySortedList = queryResultsList.OrderBy(x => x).ToList(); - if (dataSortedList.Count != querySortedList.Count) - { - resultMatched = false; - } - else - { - for (int i = 0; i < dataSortedList.Count; ++i) - { - if (Math.Abs(dataSortedList[i] - (dynamic)querySortedList[i]) > (dynamic)Convert.ChangeType(Epsilon, dataType)) - { - resultMatched = false; - break; - } - } - } - - if (!resultMatched) - { - actualStr = JsonConvert.SerializeObject(querySortedList); - expectedStr = JsonConvert.SerializeObject(dataSortedList); - } - } - else - { - List dataNotQuery = dataResultsList.Except(queryResultsList).ToList(); - List queryNotData = queryResultsList.Except(dataResultsList).ToList(); - resultMatched &= !dataNotQuery.Any() && !queryNotData.Any(); - } - } - - string assertMsg = string.Empty; - if (!resultMatched) - { - actualStr ??= JsonConvert.SerializeObject(queryResultsList); - expectedStr ??= JsonConvert.SerializeObject(dataResultsList); - - resultMatched |= actualStr.Equals(expectedStr); - if (!resultMatched) - { - assertMsg = $"Expected: {expectedStr}, Actual: {actualStr}, RandomSeed: {LinqTestInput.RandomSeed}"; - } - } - - Assert.IsTrue(resultMatched, assertMsg); - } - - /// - /// Generate a random string containing alphabetical characters - /// - /// - /// - /// a random string - public static string RandomString(Random random, int length) - { - const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz "; - return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); - } - - /// - /// Generate a random DateTime object from a DateTime, - /// with the variance of the time span between the provided DateTime to the current time - /// - /// - /// - /// - public static DateTime RandomDateTime(Random random, DateTime midDateTime) - { - TimeSpan timeSpan = DateTime.Now - midDateTime; - TimeSpan newSpan = new TimeSpan(0, random.Next(0, (int)timeSpan.TotalMinutes * 2) - (int)timeSpan.TotalMinutes, 0); - DateTime newDate = midDateTime + newSpan; - return newDate; - } - - /// - /// Generate test data for most LINQ tests - /// - /// the object type - /// the lamda to create an instance of test data - /// number of test data to be created - /// the target container - /// a lambda that takes a boolean which indicate where the query should run against CosmosDB or against original data, and return a query results as IQueryable - public static Func> GenerateTestCosmosData(Func func, int count, Container container) - { - List data = new List(); - int seed = DateTime.Now.Millisecond; - Random random = new Random(seed); - Debug.WriteLine("Random seed: {0}", seed); - LinqTestInput.RandomSeed = seed; - for (int i = 0; i < count; ++i) - { - data.Add(func(random)); - } - - foreach (T obj in data) - { - ItemResponse response = container.CreateItemAsync(obj, new Cosmos.PartitionKey("Test")).Result; - } - - FeedOptions feedOptions = new FeedOptions() { EnableScanInQuery = true, EnableCrossPartitionQuery = true }; - QueryRequestOptions requestOptions = new QueryRequestOptions() { EnableOptimisticDirectExecution = false }; - - IOrderedQueryable query = container.GetItemLinqQueryable(allowSynchronousQueryExecution: true, requestOptions: requestOptions); - - // To cover both query against backend and queries on the original data using LINQ nicely, - // the LINQ expression should be written once and they should be compiled and executed against the two sources. - // That is done by using Func that take a boolean Func. The parameter of the Func indicate whether the Cosmos DB query - // or the data list should be used. When a test is executed, the compiled LINQ expression would pass different values - // to this getQuery method. - IQueryable getQuery(bool useQuery) => useQuery ? query : data.AsQueryable(); - - return getQuery; - } - - /// - /// Generate a non-random payload for serializer LINQ tests. - /// - /// the object type - /// the lamda to create an instance of test data - /// number of test data to be created - /// the target container - /// if theCosmosLinqSerializerOption of camelCaseSerialization should be applied - /// a lambda that takes a boolean which indicate where the query should run against CosmosDB or against original data, and return a query results as IQueryable. - public static Func> GenerateSerializationTestCosmosData(Func func, int count, Container container, CosmosLinqSerializerOptions linqSerializerOptions) - { - List data = new List(); - for (int i = 0; i < count; i++) - { - data.Add(func(i, linqSerializerOptions.PropertyNamingPolicy == CosmosPropertyNamingPolicy.CamelCase)); - } - - foreach (T obj in data) - { - ItemResponse response = container.CreateItemAsync(obj, new Cosmos.PartitionKey("Test")).Result; - } - - FeedOptions feedOptions = new FeedOptions() { EnableScanInQuery = true, EnableCrossPartitionQuery = true }; - QueryRequestOptions requestOptions = new QueryRequestOptions() { EnableOptimisticDirectExecution = false }; - - IOrderedQueryable query = container.GetItemLinqQueryable(allowSynchronousQueryExecution: true, requestOptions: requestOptions, linqSerializerOptions: linqSerializerOptions); - - IQueryable getQuery(bool useQuery) => useQuery ? query : data.AsQueryable(); - - return getQuery; - } - - public static Func> GenerateFamilyCosmosData( - Cosmos.Database cosmosDatabase, out Container container) - { - // The test collection should have range index on string properties - // for the orderby tests - PartitionKeyDefinition partitionKeyDefinition = new PartitionKeyDefinition { Paths = new System.Collections.ObjectModel.Collection(new[] { "/Pk" }), Kind = PartitionKind.Hash }; - ContainerProperties newCol = new ContainerProperties() - { - Id = Guid.NewGuid().ToString(), - PartitionKey = partitionKeyDefinition, - IndexingPolicy = new Microsoft.Azure.Cosmos.IndexingPolicy() - { - IncludedPaths = new Collection() - { - new Cosmos.IncludedPath() - { - Path = "/*", - Indexes = new System.Collections.ObjectModel.Collection() - { - Microsoft.Azure.Cosmos.Index.Range(Microsoft.Azure.Cosmos.DataType.Number, -1), - Microsoft.Azure.Cosmos.Index.Range(Microsoft.Azure.Cosmos.DataType.String, -1) - } - } - }, - CompositeIndexes = new Collection>() - { - new Collection() - { - new Cosmos.CompositePath() { Path = "/FamilyId", Order = Cosmos.CompositePathSortOrder.Ascending }, - new Cosmos.CompositePath() { Path = "/Int", Order = Cosmos.CompositePathSortOrder.Ascending } - }, - new Collection() - { - new Cosmos.CompositePath() { Path = "/FamilyId", Order = Cosmos.CompositePathSortOrder.Ascending }, - new Cosmos.CompositePath() { Path = "/Int", Order = Cosmos.CompositePathSortOrder.Descending } - }, - new Collection() - { - new Cosmos.CompositePath() { Path = "/FamilyId", Order = Cosmos.CompositePathSortOrder.Ascending }, - new Cosmos.CompositePath() { Path = "/Int", Order = Cosmos.CompositePathSortOrder.Ascending }, - new Cosmos.CompositePath() { Path = "/IsRegistered", Order = Cosmos.CompositePathSortOrder.Descending } - }, - new Collection() - { - new Cosmos.CompositePath() { Path = "/Int", Order = Cosmos.CompositePathSortOrder.Ascending }, - new Cosmos.CompositePath() { Path = "/IsRegistered", Order = Cosmos.CompositePathSortOrder.Descending } - }, - new Collection() - { - new Cosmos.CompositePath() { Path = "/IsRegistered", Order = Cosmos.CompositePathSortOrder.Ascending }, - new Cosmos.CompositePath() { Path = "/Int", Order = Cosmos.CompositePathSortOrder.Descending } - } - } - } - }; - container = cosmosDatabase.CreateContainerAsync(newCol).Result; - const int Records = 100; - const int MaxNameLength = 100; - const int MaxThingStringLength = 50; - const int MaxChild = 5; - const int MaxPets = MaxChild; - const int MaxThings = MaxChild; - const int MaxGrade = 101; - const int MaxTransaction = 20; - const int MaxTransactionMinuteRange = 200; - int MaxTransactionType = Enum.GetValues(typeof(TransactionType)).Length; - Family createDataObj(Random random) - { - Family obj = new Family - { - FamilyId = random.NextDouble() < 0.05 ? "some id" : Guid.NewGuid().ToString(), - IsRegistered = random.NextDouble() < 0.5, - NullableInt = random.NextDouble() < 0.5 ? (int?)random.Next() : null, - Int = random.NextDouble() < 0.5 ? 5 : random.Next(), - Id = Guid.NewGuid().ToString(), - Pk = "Test", - Parents = new Parent[random.Next(2) + 1] - }; - for (int i = 0; i < obj.Parents.Length; ++i) - { - obj.Parents[i] = new Parent() - { - FamilyName = LinqTestsCommon.RandomString(random, random.Next(MaxNameLength)), - GivenName = LinqTestsCommon.RandomString(random, random.Next(MaxNameLength)) - }; - } - - obj.Tags = new string[random.Next(MaxChild)]; - for (int i = 0; i < obj.Tags.Length; ++i) - { - obj.Tags[i] = (i + random.Next(30, 36)).ToString(); - } - - obj.Children = new Child[random.Next(MaxChild)]; - for (int i = 0; i < obj.Children.Length; ++i) - { - obj.Children[i] = new Child() - { - Gender = random.NextDouble() < 0.5 ? "male" : "female", - FamilyName = obj.Parents[random.Next(obj.Parents.Length)].FamilyName, - GivenName = LinqTestsCommon.RandomString(random, random.Next(MaxNameLength)), - Grade = random.Next(MaxGrade) - }; - - obj.Children[i].Pets = new List(); - for (int j = 0; j < random.Next(MaxPets); ++j) - { - obj.Children[i].Pets.Add(new Pet() - { - GivenName = random.NextDouble() < 0.5 ? - LinqTestsCommon.RandomString(random, random.Next(MaxNameLength)) : - "Fluffy" - }); - } - - obj.Children[i].Things = new Dictionary(); - for (int j = 0; j < random.Next(MaxThings) + 1; ++j) - { - obj.Children[i].Things.Add( - j == 0 ? "A" : $"{j}-{random.Next()}", - LinqTestsCommon.RandomString(random, random.Next(MaxThingStringLength))); - } - } - - obj.Records = new Logs - { - LogId = LinqTestsCommon.RandomString(random, random.Next(MaxNameLength)), - Transactions = new Transaction[random.Next(MaxTransaction)] - }; - for (int i = 0; i < obj.Records.Transactions.Length; ++i) - { - Transaction transaction = new Transaction() - { - Amount = random.Next(), - Date = DateTime.Now.AddMinutes(random.Next(MaxTransactionMinuteRange)), - Type = (TransactionType)random.Next(MaxTransactionType) - }; - obj.Records.Transactions[i] = transaction; - } - - return obj; - } - - Func> getQuery = LinqTestsCommon.GenerateTestCosmosData(createDataObj, Records, container); - return getQuery; - } - + foreach (object obj in queryResults) + { + if (!dataResults.Any(a => a.Equals(obj))) + { + resultMatched = false; + return false; + } + } + + foreach (dynamic obj in dataResults) + { + if (!queryResults.Any(a => a.Equals(obj))) + { + resultMatched = false; + break; + } + } + + return resultMatched; + } + + /// + /// Compare 2 IEnumerable which may contain IEnumerable themselves. + /// + /// The query results from Cosmos DB + /// The query results from actual data + /// True if the two IEbumerable equal + private static bool NestedListsSequenceEqual(IEnumerable queryResults, IEnumerable dataResults) + { + IEnumerator queryIter, dataIter; + for (queryIter = queryResults.GetEnumerator(), dataIter = dataResults.GetEnumerator(); + queryIter.MoveNext() && dataIter.MoveNext();) + { + IEnumerable queryEnumerable = queryIter.Current as IEnumerable; + IEnumerable dataEnumerable = dataIter.Current as IEnumerable; + if (queryEnumerable == null && dataEnumerable == null) + { + if (!queryIter.Current.Equals(dataIter.Current)) return false; + + } + + else if (queryEnumerable == null || dataEnumerable == null) + { + return false; + } + + else + { + if (!LinqTestsCommon.NestedListsSequenceEqual(queryEnumerable, dataEnumerable)) return false; + } + } + + return !(queryIter.MoveNext() || dataIter.MoveNext()); + } + + /// + /// Compare the list of results from CosmosDB query and the list of results from LinQ query on the original data + /// Similar to Collections.SequenceEqual with the assumption that these lists are non-empty + /// + /// A list representing the query restuls from CosmosDB + /// A list representing the linQ query results from the original data + /// true if the two + private static bool CompareListOfArrays(List queryResults, List dataResults) + { + if (NestedListsSequenceEqual(queryResults, dataResults)) return true; + + bool resultMatched = true; + + // dataResults contains type ConcatIterator whereas queryResults may contain IEnumerable + // therefore it's simpler to just cast them into List> manually for simplify the verification + List> l1 = new List>(); + foreach (IEnumerable list in dataResults) + { + List l = new List(); + IEnumerator iterator = list.GetEnumerator(); + while (iterator.MoveNext()) + { + l.Add(iterator.Current); + } + + l1.Add(l); + } + + List> l2 = new List>(); + foreach (IEnumerable list in queryResults) + { + List l = new List(); + IEnumerator iterator = list.GetEnumerator(); + while (iterator.MoveNext()) + { + l.Add(iterator.Current); + } + + l2.Add(l); + } + + foreach (IEnumerable list in l1) + { + if (!l2.Any(a => a.SequenceEqual(list))) + { + resultMatched = false; + return false; + } + } + + foreach (IEnumerable list in l2) + { + if (!l1.Any(a => a.SequenceEqual(list))) + { + resultMatched = false; + break; + } + } + + return resultMatched; + } + + private static bool IsNumber(dynamic value) + { + return value is sbyte + || value is byte + || value is short + || value is ushort + || value is int + || value is uint + || value is long + || value is ulong + || value is float + || value is double + || value is decimal; + } + + public static Boolean IsAnonymousType(Type type) + { + Boolean hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0; + Boolean nameContainsAnonymousType = type.FullName.Contains("AnonymousType"); + Boolean isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType; + + return isAnonymousType; + } + + /// + /// Gets the results of CosmosDB query and the results of LINQ query on the original data + /// + /// + /// + public static (List queryResults, List dataResults) GetResults(IQueryable queryResults, IQueryable dataResults) + { + // execution validation + IEnumerator queryEnumerator = queryResults.GetEnumerator(); + List queryResultsList = new List(); + while (queryEnumerator.MoveNext()) + { + queryResultsList.Add(queryEnumerator.Current); + } + + List dataResultsList = dataResults?.Cast()?.ToList(); + + return (queryResultsList, dataResultsList); + } + + /// + /// Validates the results of CosmosDB query and the results of LINQ query on the original data + /// Using Assert, will fail the unit test if the two results list are not SequenceEqual + /// + /// + /// + private static void ValidateResults(List queryResultsList, List dataResultsList, bool ignoreOrder) + { + bool resultMatched = true; + string actualStr = null; + string expectedStr = null; + if (dataResultsList.Count == 0 || queryResultsList.Count == 0) + { + resultMatched &= dataResultsList.Count == queryResultsList.Count; + } + else + { + dynamic firstElem = dataResultsList.FirstOrDefault(); + if (firstElem is IEnumerable) + { + resultMatched &= CompareListOfArrays(queryResultsList, dataResultsList); + } + else if (LinqTestsCommon.IsAnonymousType(firstElem.GetType())) + { + resultMatched &= CompareListOfAnonymousType(queryResultsList, dataResultsList, ignoreOrder); + } + else if (LinqTestsCommon.IsNumber(firstElem)) + { + const double Epsilon = 1E-6; + Type dataType = firstElem.GetType(); + List dataSortedList = dataResultsList.OrderBy(x => x).ToList(); + List querySortedList = queryResultsList.OrderBy(x => x).ToList(); + if (dataSortedList.Count != querySortedList.Count) + { + resultMatched = false; + } + else + { + for (int i = 0; i < dataSortedList.Count; ++i) + { + if (Math.Abs(dataSortedList[i] - (dynamic)querySortedList[i]) > (dynamic)Convert.ChangeType(Epsilon, dataType)) + { + resultMatched = false; + break; + } + } + } + + if (!resultMatched) + { + actualStr = JsonConvert.SerializeObject(querySortedList); + expectedStr = JsonConvert.SerializeObject(dataSortedList); + } + } + else + { + List dataNotQuery = dataResultsList.Except(queryResultsList).ToList(); + List queryNotData = queryResultsList.Except(dataResultsList).ToList(); + resultMatched &= !dataNotQuery.Any() && !queryNotData.Any(); + } + } + + string assertMsg = string.Empty; + if (!resultMatched) + { + actualStr ??= JsonConvert.SerializeObject(queryResultsList); + expectedStr ??= JsonConvert.SerializeObject(dataResultsList); + + resultMatched |= actualStr.Equals(expectedStr); + if (!resultMatched) + { + assertMsg = $"Expected: {expectedStr}, Actual: {actualStr}, RandomSeed: {LinqTestInput.RandomSeed}"; + } + } + + Assert.IsTrue(resultMatched, assertMsg); + } + + /// + /// Generate a random string containing alphabetical characters + /// + /// + /// + /// a random string + public static string RandomString(Random random, int length) + { + const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz "; + return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); + } + + /// + /// Generate a random DateTime object from a DateTime, + /// with the variance of the time span between the provided DateTime to the current time + /// + /// + /// + /// + public static DateTime RandomDateTime(Random random, DateTime midDateTime) + { + TimeSpan timeSpan = DateTime.Now - midDateTime; + TimeSpan newSpan = new TimeSpan(0, random.Next(0, (int)timeSpan.TotalMinutes * 2) - (int)timeSpan.TotalMinutes, 0); + DateTime newDate = midDateTime + newSpan; + return newDate; + } + + /// + /// Generate test data for most LINQ tests + /// + /// the object type + /// the lamda to create an instance of test data + /// number of test data to be created + /// the target container + /// a lambda that takes a boolean which indicate where the query should run against CosmosDB or against original data, and return a query results as IQueryable + public static Func> GenerateTestCosmosData(Func func, int count, Container container) + { + List data = new List(); + int seed = DateTime.Now.Millisecond; + Random random = new Random(seed); + Debug.WriteLine("Random seed: {0}", seed); + LinqTestInput.RandomSeed = seed; + for (int i = 0; i < count; ++i) + { + data.Add(func(random)); + } + + foreach (T obj in data) + { + ItemResponse response = container.CreateItemAsync(obj, new Cosmos.PartitionKey("Test")).Result; + } + + FeedOptions feedOptions = new FeedOptions() { EnableScanInQuery = true, EnableCrossPartitionQuery = true }; + QueryRequestOptions requestOptions = new QueryRequestOptions() { EnableOptimisticDirectExecution = false }; + + IOrderedQueryable query = container.GetItemLinqQueryable(allowSynchronousQueryExecution: true, requestOptions: requestOptions); + + // To cover both query against backend and queries on the original data using LINQ nicely, + // the LINQ expression should be written once and they should be compiled and executed against the two sources. + // That is done by using Func that take a boolean Func. The parameter of the Func indicate whether the Cosmos DB query + // or the data list should be used. When a test is executed, the compiled LINQ expression would pass different values + // to this getQuery method. + IQueryable getQuery(bool useQuery) => useQuery ? query : data.AsQueryable(); + + return getQuery; + } + + /// + /// Generate a non-random payload for serializer LINQ tests. + /// + /// the object type + /// the lamda to create an instance of test data + /// number of test data to be created + /// the target container + /// if theCosmosLinqSerializerOption of camelCaseSerialization should be applied + /// a lambda that takes a boolean which indicate where the query should run against CosmosDB or against original data, and return a query results as IQueryable. + public static Func> GenerateSerializationTestCosmosData(Func func, int count, Container container, CosmosLinqSerializerOptions linqSerializerOptions) + { + List data = new List(); + for (int i = 0; i < count; i++) + { + data.Add(func(i, linqSerializerOptions.PropertyNamingPolicy == CosmosPropertyNamingPolicy.CamelCase)); + } + + foreach (T obj in data) + { + ItemResponse response = container.CreateItemAsync(obj, new Cosmos.PartitionKey("Test")).Result; + } + + FeedOptions feedOptions = new FeedOptions() { EnableScanInQuery = true, EnableCrossPartitionQuery = true }; + QueryRequestOptions requestOptions = new QueryRequestOptions() { EnableOptimisticDirectExecution = false }; + + IOrderedQueryable query = container.GetItemLinqQueryable(allowSynchronousQueryExecution: true, requestOptions: requestOptions, linqSerializerOptions: linqSerializerOptions); + + IQueryable getQuery(bool useQuery) => useQuery ? query : data.AsQueryable(); + + return getQuery; + } + + public static Func> GenerateFamilyCosmosData( + Cosmos.Database cosmosDatabase, out Container container) + { + // The test collection should have range index on string properties + // for the orderby tests + PartitionKeyDefinition partitionKeyDefinition = new PartitionKeyDefinition { Paths = new System.Collections.ObjectModel.Collection(new[] { "/Pk" }), Kind = PartitionKind.Hash }; + ContainerProperties newCol = new ContainerProperties() + { + Id = Guid.NewGuid().ToString(), + PartitionKey = partitionKeyDefinition, + IndexingPolicy = new Microsoft.Azure.Cosmos.IndexingPolicy() + { + IncludedPaths = new Collection() + { + new Cosmos.IncludedPath() + { + Path = "/*", + Indexes = new System.Collections.ObjectModel.Collection() + { + Microsoft.Azure.Cosmos.Index.Range(Microsoft.Azure.Cosmos.DataType.Number, -1), + Microsoft.Azure.Cosmos.Index.Range(Microsoft.Azure.Cosmos.DataType.String, -1) + } + } + }, + CompositeIndexes = new Collection>() + { + new Collection() + { + new Cosmos.CompositePath() { Path = "/FamilyId", Order = Cosmos.CompositePathSortOrder.Ascending }, + new Cosmos.CompositePath() { Path = "/Int", Order = Cosmos.CompositePathSortOrder.Ascending } + }, + new Collection() + { + new Cosmos.CompositePath() { Path = "/FamilyId", Order = Cosmos.CompositePathSortOrder.Ascending }, + new Cosmos.CompositePath() { Path = "/Int", Order = Cosmos.CompositePathSortOrder.Descending } + }, + new Collection() + { + new Cosmos.CompositePath() { Path = "/FamilyId", Order = Cosmos.CompositePathSortOrder.Ascending }, + new Cosmos.CompositePath() { Path = "/Int", Order = Cosmos.CompositePathSortOrder.Ascending }, + new Cosmos.CompositePath() { Path = "/IsRegistered", Order = Cosmos.CompositePathSortOrder.Descending } + }, + new Collection() + { + new Cosmos.CompositePath() { Path = "/Int", Order = Cosmos.CompositePathSortOrder.Ascending }, + new Cosmos.CompositePath() { Path = "/IsRegistered", Order = Cosmos.CompositePathSortOrder.Descending } + }, + new Collection() + { + new Cosmos.CompositePath() { Path = "/IsRegistered", Order = Cosmos.CompositePathSortOrder.Ascending }, + new Cosmos.CompositePath() { Path = "/Int", Order = Cosmos.CompositePathSortOrder.Descending } + } + } + } + }; + container = cosmosDatabase.CreateContainerAsync(newCol).Result; + const int Records = 100; + const int MaxNameLength = 100; + const int MaxThingStringLength = 50; + const int MaxChild = 5; + const int MaxPets = MaxChild; + const int MaxThings = MaxChild; + const int MaxGrade = 101; + const int MaxTransaction = 20; + const int MaxTransactionMinuteRange = 200; + int MaxTransactionType = Enum.GetValues(typeof(TransactionType)).Length; + Family createDataObj(Random random) + { + Family obj = new Family + { + FamilyId = random.NextDouble() < 0.05 ? "some id" : Guid.NewGuid().ToString(), + IsRegistered = random.NextDouble() < 0.5, + NullableInt = random.NextDouble() < 0.5 ? (int?)random.Next() : null, + Int = random.NextDouble() < 0.5 ? 5 : random.Next(), + Id = Guid.NewGuid().ToString(), + Pk = "Test", + Parents = new Parent[random.Next(2) + 1] + }; + for (int i = 0; i < obj.Parents.Length; ++i) + { + obj.Parents[i] = new Parent() + { + FamilyName = LinqTestsCommon.RandomString(random, random.Next(MaxNameLength)), + GivenName = LinqTestsCommon.RandomString(random, random.Next(MaxNameLength)) + }; + } + + obj.Tags = new string[random.Next(MaxChild)]; + for (int i = 0; i < obj.Tags.Length; ++i) + { + obj.Tags[i] = (i + random.Next(30, 36)).ToString(); + } + + obj.Children = new Child[random.Next(MaxChild)]; + for (int i = 0; i < obj.Children.Length; ++i) + { + obj.Children[i] = new Child() + { + Gender = random.NextDouble() < 0.5 ? "male" : "female", + FamilyName = obj.Parents[random.Next(obj.Parents.Length)].FamilyName, + GivenName = LinqTestsCommon.RandomString(random, random.Next(MaxNameLength)), + Grade = random.Next(MaxGrade) + }; + + obj.Children[i].Pets = new List(); + for (int j = 0; j < random.Next(MaxPets); ++j) + { + obj.Children[i].Pets.Add(new Pet() + { + GivenName = random.NextDouble() < 0.5 ? + LinqTestsCommon.RandomString(random, random.Next(MaxNameLength)) : + "Fluffy" + }); + } + + obj.Children[i].Things = new Dictionary(); + for (int j = 0; j < random.Next(MaxThings) + 1; ++j) + { + obj.Children[i].Things.Add( + j == 0 ? "A" : $"{j}-{random.Next()}", + LinqTestsCommon.RandomString(random, random.Next(MaxThingStringLength))); + } + } + + obj.Records = new Logs + { + LogId = LinqTestsCommon.RandomString(random, random.Next(MaxNameLength)), + Transactions = new Transaction[random.Next(MaxTransaction)] + }; + for (int i = 0; i < obj.Records.Transactions.Length; ++i) + { + Transaction transaction = new Transaction() + { + Amount = random.Next(), + Date = DateTime.Now.AddMinutes(random.Next(MaxTransactionMinuteRange)), + Type = (TransactionType)random.Next(MaxTransactionType) + }; + obj.Records.Transactions[i] = transaction; + } + + return obj; + } + + Func> getQuery = LinqTestsCommon.GenerateTestCosmosData(createDataObj, Records, container); + return getQuery; + } + public static Func> GenerateSimpleCosmosData(Cosmos.Database cosmosDatabase, bool useRandomData = true) - { - const int DocumentCount = 10; - PartitionKeyDefinition partitionKeyDefinition = new PartitionKeyDefinition { Paths = new System.Collections.ObjectModel.Collection(new[] { "/Pk" }), Kind = PartitionKind.Hash }; - Container container = cosmosDatabase.CreateContainerAsync(new ContainerProperties { Id = Guid.NewGuid().ToString(), PartitionKey = partitionKeyDefinition }).Result; - + { + const int DocumentCount = 10; + PartitionKeyDefinition partitionKeyDefinition = new PartitionKeyDefinition { Paths = new System.Collections.ObjectModel.Collection(new[] { "/Pk" }), Kind = PartitionKind.Hash }; + Container container = cosmosDatabase.CreateContainerAsync(new ContainerProperties { Id = Guid.NewGuid().ToString(), PartitionKey = partitionKeyDefinition }).Result; + ILinqTestDataGenerator dataGenerator = useRandomData ? new LinqTestRandomDataGenerator(DocumentCount) : new LinqTestDataGenerator(DocumentCount); List testData = new List(dataGenerator.GenerateData()); foreach (Data dataEntry in testData) - { - Data response = container.CreateItemAsync(dataEntry, new Cosmos.PartitionKey(dataEntry.Pk)).Result; - } - - FeedOptions feedOptions = new FeedOptions() { EnableScanInQuery = true, EnableCrossPartitionQuery = true }; - QueryRequestOptions requestOptions = new QueryRequestOptions() { EnableOptimisticDirectExecution = false }; - - IOrderedQueryable query = container.GetItemLinqQueryable(allowSynchronousQueryExecution: true, requestOptions: requestOptions); - - // To cover both query against backend and queries on the original data using LINQ nicely, - // the LINQ expression should be written once and they should be compiled and executed against the two sources. - // That is done by using Func that take a boolean Func. The parameter of the Func indicate whether the Cosmos DB query - // or the data list should be used. When a test is executed, the compiled LINQ expression would pass different values - // to this getQuery method. - IQueryable getQuery(bool useQuery) => useQuery ? query : testData.AsQueryable(); - return getQuery; - } - - public static LinqTestOutput ExecuteTest(LinqTestInput input, bool serializeResultsInBaseline = false) - { - string querySqlStr = string.Empty; - try - { - Func compiledQuery = input.Expression.Compile(); - - IQueryable query = compiledQuery(true); - querySqlStr = JObject.Parse(query.ToString()).GetValue("query", StringComparison.Ordinal).ToString(); - - IQueryable dataQuery = input.skipVerification ? null : compiledQuery(false); - - (List queryResults, List dataResults) = GetResults(query, dataQuery); - - // we skip unordered query because the LINQ results vs actual query results are non-deterministic - if (!input.skipVerification) - { - LinqTestsCommon.ValidateResults(queryResults, dataResults, input.ignoreOrder); - } - - string serializedResults = serializeResultsInBaseline ? - JsonConvert.SerializeObject(queryResults.Select(item => item is LinqTestObject ? item.ToString() : item), new JsonSerializerSettings { Formatting = Newtonsoft.Json.Formatting.Indented}) : - null; - - return new LinqTestOutput(querySqlStr, serializedResults, errorMsg: null, input.inputData); - } - catch (Exception e) - { - return new LinqTestOutput(querySqlStr, serializedResults: null, errorMsg: LinqTestsCommon.BuildExceptionMessageForTest(e), inputData: input.inputData); - } - } - - public static string BuildExceptionMessageForTest(Exception ex) + { + Data response = container.CreateItemAsync(dataEntry, new Cosmos.PartitionKey(dataEntry.Pk)).Result; + } + + FeedOptions feedOptions = new FeedOptions() { EnableScanInQuery = true, EnableCrossPartitionQuery = true }; + QueryRequestOptions requestOptions = new QueryRequestOptions() { EnableOptimisticDirectExecution = false }; + + IOrderedQueryable query = container.GetItemLinqQueryable(allowSynchronousQueryExecution: true, requestOptions: requestOptions); + + // To cover both query against backend and queries on the original data using LINQ nicely, + // the LINQ expression should be written once and they should be compiled and executed against the two sources. + // That is done by using Func that take a boolean Func. The parameter of the Func indicate whether the Cosmos DB query + // or the data list should be used. When a test is executed, the compiled LINQ expression would pass different values + // to this getQuery method. + IQueryable getQuery(bool useQuery) => useQuery ? query : testData.AsQueryable(); + return getQuery; + } + + public static LinqTestOutput ExecuteTest(LinqTestInput input, bool serializeResultsInBaseline = false) + { + string querySqlStr = string.Empty; + try + { + Func compiledQuery = input.Expression.Compile(); + + IQueryable query = compiledQuery(true); + querySqlStr = JObject.Parse(query.ToString()).GetValue("query", StringComparison.Ordinal).ToString(); + + IQueryable dataQuery = input.skipVerification ? null : compiledQuery(false); + + (List queryResults, List dataResults) = GetResults(query, dataQuery); + + // we skip unordered query because the LINQ results vs actual query results are non-deterministic + if (!input.skipVerification) + { + LinqTestsCommon.ValidateResults(queryResults, dataResults, input.ignoreOrder); + } + + string serializedResults = serializeResultsInBaseline ? + JsonConvert.SerializeObject(queryResults.Select(item => item is LinqTestObject ? item.ToString() : item), new JsonSerializerSettings { Formatting = Newtonsoft.Json.Formatting.Indented}) : + null; + + return new LinqTestOutput(querySqlStr, serializedResults, errorMsg: null, input.inputData); + } + catch (Exception e) + { + return new LinqTestOutput(querySqlStr, serializedResults: null, errorMsg: LinqTestsCommon.BuildExceptionMessageForTest(e), inputData: input.inputData); + } + } + + public static string BuildExceptionMessageForTest(Exception ex) { StringBuilder message = new StringBuilder(); - do - { - if (ex is CosmosException cosmosException) + do + { + if (ex is CosmosException cosmosException) { message.Append($"Status Code: {cosmosException.StatusCode}"); - } - else if (ex is DocumentClientException documentClientException) - { - message.Append(documentClientException.RawErrorMessage); - } - else + } + else if (ex is DocumentClientException documentClientException) + { + message.Append(documentClientException.RawErrorMessage); + } + else { message.Append(ex.Message); - } - - ex = ex.InnerException; - if (ex != null) - { - message.Append(","); - } - } + } + + ex = ex.InnerException; + if (ex != null) + { + message.Append(","); + } + } while (ex != null); return message.ToString(); - } - } - - /// - /// A base class that determines equality based on its json representation - /// - public class LinqTestObject - { - private string json; - - protected virtual string SerializeForTestBaseline() - { - return JsonConvert.SerializeObject(this); - } - - public override string ToString() - { - // simple cached serialization - this.json ??= this.SerializeForTestBaseline(); - return this.json; - } - - public override bool Equals(object obj) - { - if (!(obj is LinqTestObject && - obj.GetType().IsAssignableFrom(this.GetType()) && - this.GetType().IsAssignableFrom(obj.GetType()))) return false; - if (obj == null) return false; - - return this.ToString().Equals(obj.ToString()); - } - - public override int GetHashCode() - { - return this.ToString().GetHashCode(); - } - } - - public class LinqTestInput : BaselineTestInput - { - internal static Regex classNameRegex = new Regex("(value\\(.+?\\+)?\\<\\>.+?__([A-Za-z]+)((\\d+_\\d+(`\\d+\\[.+?\\])?\\)(\\.value)?)|\\d+`\\d+)"); - internal static Regex invokeCompileRegex = new Regex("(Convert\\()?Invoke\\([^.]+\\.[^.,]+(\\.Compile\\(\\))?, b\\)(\\.Cast\\(\\))?(\\))?"); - - // As the tests are executed sequentially - // We can store the random seed in a static variable for diagnostics - internal static int RandomSeed = -1; - - internal int randomSeed = -1; - internal Expression> Expression { get; } - internal string expressionStr; - internal string inputData; - - // We skip the verification between Cosmos DB and actual query restuls in the following cases - // - unordered query since the results are not deterministics for LinQ results and actual query results - // - scenarios not supported in LINQ, e.g. sequence doesn't contain element. - internal bool skipVerification; + } + } + + /// + /// A base class that determines equality based on its json representation + /// + public class LinqTestObject + { + private string json; + + protected virtual string SerializeForTestBaseline() + { + return JsonConvert.SerializeObject(this); + } + + public override string ToString() + { + // simple cached serialization + this.json ??= this.SerializeForTestBaseline(); + return this.json; + } + + public override bool Equals(object obj) + { + if (!(obj is LinqTestObject && + obj.GetType().IsAssignableFrom(this.GetType()) && + this.GetType().IsAssignableFrom(obj.GetType()))) return false; + if (obj == null) return false; + + return this.ToString().Equals(obj.ToString()); + } + + public override int GetHashCode() + { + return this.ToString().GetHashCode(); + } + } + + public class LinqTestInput : BaselineTestInput + { + internal static Regex classNameRegex = new Regex("(value\\(.+?\\+)?\\<\\>.+?__([A-Za-z]+)((\\d+_\\d+(`\\d+\\[.+?\\])?\\)(\\.value)?)|\\d+`\\d+)"); + internal static Regex invokeCompileRegex = new Regex("(Convert\\()?Invoke\\([^.]+\\.[^.,]+(\\.Compile\\(\\))?, b\\)(\\.Cast\\(\\))?(\\))?"); + + // As the tests are executed sequentially + // We can store the random seed in a static variable for diagnostics + internal static int RandomSeed = -1; + + internal int randomSeed = -1; + internal Expression> Expression { get; } + internal string expressionStr; + internal string inputData; + + // We skip the verification between Cosmos DB and actual query restuls in the following cases + // - unordered query since the results are not deterministics for LinQ results and actual query results + // - scenarios not supported in LINQ, e.g. sequence doesn't contain element. + internal bool skipVerification; // Ignore Ordering for AnonymousType object internal bool ignoreOrder; internal bool serializeOutput; - - internal LinqTestInput( - string description, - Expression> expr, + + internal LinqTestInput( + string description, + Expression> expr, bool skipVerification = false, - bool ignoreOrder = false, - string expressionStr = null, + bool ignoreOrder = false, + string expressionStr = null, string inputData = null, - bool serializeOutput = false) - : base(description) - { - this.Expression = expr ?? throw new ArgumentNullException($"{nameof(expr)} must not be null."); + bool serializeOutput = false) + : base(description) + { + this.Expression = expr ?? throw new ArgumentNullException($"{nameof(expr)} must not be null."); this.skipVerification = skipVerification; - this.ignoreOrder = ignoreOrder; - this.expressionStr = expressionStr; + this.ignoreOrder = ignoreOrder; + this.expressionStr = expressionStr; this.inputData = inputData; - this.serializeOutput = serializeOutput; - } - - public static string FilterInputExpression(string input) - { - StringBuilder expressionSb = new StringBuilder(input); - // simplify full qualified class name - // e.g. before: value(Microsoft.Azure.Documents.Services.Management.Tests.LinqSQLTranslationTest+<>c__DisplayClass7_0), after: DisplayClass - // before: <>f__AnonymousType14`2(, after: AnonymousType( - // value(Microsoft.Azure.Documents.Services.Management.Tests.LinqProviderTests.LinqTranslationBaselineTests +<> c__DisplayClass24_0`1[System.String]).value - Match match = classNameRegex.Match(expressionSb.ToString()); - while (match.Success) - { - expressionSb = expressionSb.Replace(match.Groups[0].Value, match.Groups[2].Value); - match = match.NextMatch(); - } - - // remove the Invoke().Compile() string from the Linq scanning tests - match = invokeCompileRegex.Match(expressionSb.ToString()); - while (match.Success) - { - expressionSb = expressionSb.Replace(match.Groups[0].Value, string.Empty); - match = match.NextMatch(); - } - - expressionSb.Insert(0, "query"); - - return expressionSb.ToString(); - } - - public override void SerializeAsXml(XmlWriter xmlWriter) - { - if (xmlWriter == null) - { - throw new ArgumentNullException($"{nameof(xmlWriter)} cannot be null."); - } - - this.expressionStr ??= LinqTestInput.FilterInputExpression(this.Expression.Body.ToString()); - - xmlWriter.WriteStartElement("Description"); - xmlWriter.WriteCData(this.Description); - xmlWriter.WriteEndElement(); - xmlWriter.WriteStartElement("Expression"); - xmlWriter.WriteCData(this.expressionStr); - xmlWriter.WriteEndElement(); - } - } - - public class LinqTestOutput : BaselineTestOutput - { - internal static Regex sdkVersion = new Regex("(,\\W*)?documentdb-dotnet-sdk[^]]+"); - internal static Regex activityId = new Regex("(,\\W*)?ActivityId:.+", RegexOptions.Multiline); - internal static Regex newLine = new Regex("(\r\n|\r|\n)"); - - internal string SqlQuery { get; } - internal string ErrorMessage { get; } - internal string Results { get; } - internal string InputData { get; } - - private static readonly Dictionary newlineKeywords = new Dictionary() { - { "SELECT", "\nSELECT" }, - { "FROM", "\nFROM" }, - { "WHERE", "\nWHERE" }, - { "JOIN", "\nJOIN" }, - { "ORDER BY", "\nORDER BY" }, + this.serializeOutput = serializeOutput; + } + + public static string FilterInputExpression(string input) + { + StringBuilder expressionSb = new StringBuilder(input); + // simplify full qualified class name + // e.g. before: value(Microsoft.Azure.Documents.Services.Management.Tests.LinqSQLTranslationTest+<>c__DisplayClass7_0), after: DisplayClass + // before: <>f__AnonymousType14`2(, after: AnonymousType( + // value(Microsoft.Azure.Documents.Services.Management.Tests.LinqProviderTests.LinqTranslationBaselineTests +<> c__DisplayClass24_0`1[System.String]).value + Match match = classNameRegex.Match(expressionSb.ToString()); + while (match.Success) + { + expressionSb = expressionSb.Replace(match.Groups[0].Value, match.Groups[2].Value); + match = match.NextMatch(); + } + + // remove the Invoke().Compile() string from the Linq scanning tests + match = invokeCompileRegex.Match(expressionSb.ToString()); + while (match.Success) + { + expressionSb = expressionSb.Replace(match.Groups[0].Value, string.Empty); + match = match.NextMatch(); + } + + expressionSb.Insert(0, "query"); + + return expressionSb.ToString(); + } + + public override void SerializeAsXml(XmlWriter xmlWriter) + { + if (xmlWriter == null) + { + throw new ArgumentNullException($"{nameof(xmlWriter)} cannot be null."); + } + + this.expressionStr ??= LinqTestInput.FilterInputExpression(this.Expression.Body.ToString()); + + xmlWriter.WriteStartElement("Description"); + xmlWriter.WriteCData(this.Description); + xmlWriter.WriteEndElement(); + xmlWriter.WriteStartElement("Expression"); + xmlWriter.WriteCData(this.expressionStr); + xmlWriter.WriteEndElement(); + } + } + + public class LinqTestOutput : BaselineTestOutput + { + internal static Regex sdkVersion = new Regex("(,\\W*)?documentdb-dotnet-sdk[^]]+"); + internal static Regex activityId = new Regex("(,\\W*)?ActivityId:.+", RegexOptions.Multiline); + internal static Regex newLine = new Regex("(\r\n|\r|\n)"); + + internal string SqlQuery { get; } + internal string ErrorMessage { get; } + internal string Results { get; } + internal string InputData { get; } + + private static readonly Dictionary newlineKeywords = new Dictionary() { + { "SELECT", "\nSELECT" }, + { "FROM", "\nFROM" }, + { "WHERE", "\nWHERE" }, + { "JOIN", "\nJOIN" }, + { "ORDER BY", "\nORDER BY" }, { "OFFSET", "\nOFFSET" }, - { "GROUP BY", "\nGROUP BY" }, - { " )", "\n)" } - }; - - public static string FormatErrorMessage(string msg) - { - msg = newLine.Replace(msg, string.Empty); - - // remove sdk version in the error message which can change in the future. - // e.g. - msg = sdkVersion.Replace(msg, string.Empty); - - // remove activity Id - msg = activityId.Replace(msg, string.Empty); - - return msg; - } - - internal LinqTestOutput(string sqlQuery, string serializedResults, string errorMsg, string inputData) - { - this.SqlQuery = FormatSql(sqlQuery); - this.Results = serializedResults; - this.ErrorMessage = errorMsg; - this.InputData = inputData; - } - - public static String FormatSql(string sqlQuery) - { - const string subqueryCue = "(SELECT"; - bool hasSubquery = sqlQuery.IndexOf(subqueryCue, StringComparison.OrdinalIgnoreCase) > 0; - - StringBuilder sb = new StringBuilder(sqlQuery); - foreach (KeyValuePair kv in newlineKeywords) - { - sb.Replace(kv.Key, kv.Value); - } - - if (!hasSubquery) return sb.ToString(); - - const string oneTab = " "; - const string startCue = "SELECT"; - const string endCue = ")"; - string[] tokens = sb.ToString().Split('\n'); - bool firstSelect = true; - sb.Length = 0; - StringBuilder indentSb = new StringBuilder(); - for (int i = 0; i < tokens.Length; ++i) - { - if (tokens[i].StartsWith(startCue, StringComparison.OrdinalIgnoreCase)) - { - if (!firstSelect) indentSb.Append(oneTab); else firstSelect = false; - - } - else if (tokens[i].StartsWith(endCue, StringComparison.OrdinalIgnoreCase)) - { - indentSb.Length -= oneTab.Length; - } - - sb.Append(indentSb).Append(tokens[i]).Append("\n"); - } - - return sb.ToString(); - } - - public override void SerializeAsXml(XmlWriter xmlWriter) - { - xmlWriter.WriteStartElement(nameof(this.SqlQuery)); - xmlWriter.WriteCData(this.SqlQuery); - xmlWriter.WriteEndElement(); - if (this.InputData != null) - { - xmlWriter.WriteStartElement("InputData"); - xmlWriter.WriteCData(this.InputData); - xmlWriter.WriteEndElement(); - } - if (this.Results != null) - { - xmlWriter.WriteStartElement("Results"); - xmlWriter.WriteCData(this.Results); - xmlWriter.WriteEndElement(); - } - if (this.ErrorMessage != null) - { - xmlWriter.WriteStartElement("ErrorMessage"); - xmlWriter.WriteCData(LinqTestOutput.FormatErrorMessage(this.ErrorMessage)); - xmlWriter.WriteEndElement(); - } - } - } - - class SystemTextJsonLinqSerializer : CosmosLinqSerializer - { + { "GROUP BY", "\nGROUP BY" }, + { " )", "\n)" } + }; + + public static string FormatErrorMessage(string msg) + { + msg = newLine.Replace(msg, string.Empty); + + // remove sdk version in the error message which can change in the future. + // e.g. + msg = sdkVersion.Replace(msg, string.Empty); + + // remove activity Id + msg = activityId.Replace(msg, string.Empty); + + return msg; + } + + internal LinqTestOutput(string sqlQuery, string serializedResults, string errorMsg, string inputData) + { + this.SqlQuery = FormatSql(sqlQuery); + this.Results = serializedResults; + this.ErrorMessage = errorMsg; + this.InputData = inputData; + } + + public static String FormatSql(string sqlQuery) + { + const string subqueryCue = "(SELECT"; + bool hasSubquery = sqlQuery.IndexOf(subqueryCue, StringComparison.OrdinalIgnoreCase) > 0; + + StringBuilder sb = new StringBuilder(sqlQuery); + foreach (KeyValuePair kv in newlineKeywords) + { + sb.Replace(kv.Key, kv.Value); + } + + if (!hasSubquery) return sb.ToString(); + + const string oneTab = " "; + const string startCue = "SELECT"; + const string endCue = ")"; + string[] tokens = sb.ToString().Split('\n'); + bool firstSelect = true; + sb.Length = 0; + StringBuilder indentSb = new StringBuilder(); + for (int i = 0; i < tokens.Length; ++i) + { + if (tokens[i].StartsWith(startCue, StringComparison.OrdinalIgnoreCase)) + { + if (!firstSelect) indentSb.Append(oneTab); else firstSelect = false; + + } + else if (tokens[i].StartsWith(endCue, StringComparison.OrdinalIgnoreCase)) + { + indentSb.Length -= oneTab.Length; + } + + sb.Append(indentSb).Append(tokens[i]).Append("\n"); + } + + return sb.ToString(); + } + + public override void SerializeAsXml(XmlWriter xmlWriter) + { + xmlWriter.WriteStartElement(nameof(this.SqlQuery)); + xmlWriter.WriteCData(this.SqlQuery); + xmlWriter.WriteEndElement(); + if (this.InputData != null) + { + xmlWriter.WriteStartElement("InputData"); + xmlWriter.WriteCData(this.InputData); + xmlWriter.WriteEndElement(); + } + if (this.Results != null) + { + xmlWriter.WriteStartElement("Results"); + xmlWriter.WriteCData(this.Results); + xmlWriter.WriteEndElement(); + } + if (this.ErrorMessage != null) + { + xmlWriter.WriteStartElement("ErrorMessage"); + xmlWriter.WriteCData(LinqTestOutput.FormatErrorMessage(this.ErrorMessage)); + xmlWriter.WriteEndElement(); + } + } + } + + internal class SystemTextJsonLinqSerializer : CosmosLinqSerializer + { private readonly JsonObjectSerializer systemTextJsonSerializer; - private readonly JsonSerializerOptions jsonSerializerOptions; - - public SystemTextJsonLinqSerializer(JsonSerializerOptions jsonSerializerOptions) - { + private readonly JsonSerializerOptions jsonSerializerOptions; + + public SystemTextJsonLinqSerializer(JsonSerializerOptions jsonSerializerOptions) + { this.systemTextJsonSerializer = new JsonObjectSerializer(jsonSerializerOptions); - this.jsonSerializerOptions = jsonSerializerOptions; - } - - public override T FromStream(Stream stream) - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - using (stream) - { - if (stream.CanSeek && stream.Length == 0) - { - return default; - } - - if (typeof(Stream).IsAssignableFrom(typeof(T))) - { - return (T)(object)stream; - } - - return (T)this.systemTextJsonSerializer.Deserialize(stream, typeof(T), default); - } - } - - public override Stream ToStream(T input) - { - MemoryStream streamPayload = new MemoryStream(); - this.systemTextJsonSerializer.Serialize(streamPayload, input, input.GetType(), default); - streamPayload.Position = 0; - return streamPayload; - } - - public override string SerializeMemberName(MemberInfo memberInfo) - { + this.jsonSerializerOptions = jsonSerializerOptions; + } + + public override T FromStream(Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + using (stream) + { + if (stream.CanSeek && stream.Length == 0) + { + return default; + } + + if (typeof(Stream).IsAssignableFrom(typeof(T))) + { + return (T)(object)stream; + } + + return (T)this.systemTextJsonSerializer.Deserialize(stream, typeof(T), default); + } + } + + public override Stream ToStream(T input) + { + MemoryStream streamPayload = new MemoryStream(); + this.systemTextJsonSerializer.Serialize(streamPayload, input, input.GetType(), default); + streamPayload.Position = 0; + return streamPayload; + } + + public override string SerializeMemberName(MemberInfo memberInfo) + { System.Text.Json.Serialization.JsonExtensionDataAttribute jsonExtensionDataAttribute = memberInfo.GetCustomAttribute(true); if (jsonExtensionDataAttribute != null) @@ -901,7 +901,7 @@ public override string SerializeMemberName(MemberInfo memberInfo) return null; } - JsonPropertyNameAttribute jsonPropertyNameAttribute = memberInfo.GetCustomAttribute(true); + JsonPropertyNameAttribute jsonPropertyNameAttribute = memberInfo.GetCustomAttribute(true); if (!string.IsNullOrEmpty(jsonPropertyNameAttribute?.Name)) { return jsonPropertyNameAttribute.Name; @@ -914,46 +914,7 @@ public override string SerializeMemberName(MemberInfo memberInfo) // Do any additional handling of JsonSerializerOptions here. - return memberInfo.Name; - } + return memberInfo.Name; + } } - - class SystemTextJsonSerializer : CosmosSerializer - { - private readonly JsonObjectSerializer systemTextJsonSerializer; - - public SystemTextJsonSerializer(JsonSerializerOptions jsonSerializerOptions) - { - this.systemTextJsonSerializer = new JsonObjectSerializer(jsonSerializerOptions); - } - - public override T FromStream(Stream stream) - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - using (stream) - { - if (stream.CanSeek && stream.Length == 0) - { - return default; - } - - if (typeof(Stream).IsAssignableFrom(typeof(T))) - { - return (T)(object)stream; - } - - return (T)this.systemTextJsonSerializer.Deserialize(stream, typeof(T), default); - } - } - - public override Stream ToStream(T input) - { - MemoryStream streamPayload = new MemoryStream(); - this.systemTextJsonSerializer.Serialize(streamPayload, input, input.GetType(), default); - streamPayload.Position = 0; - return streamPayload; - } - } -} +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqTranslationBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqTranslationBaselineTests.cs index 85242ef652..b1faf73c28 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqTranslationBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/LinqTranslationBaselineTests.cs @@ -105,6 +105,13 @@ public static bool ObjectEquals(object objA, object objB) return objA.Equals(objB); } + private static Guid GuidFromInt(int value) + { + byte[] bytes = new byte[16]; + BitConverter.GetBytes(value).CopyTo(bytes, 0); + return new Guid(bytes); + } + internal class DataObject : LinqTestObject { public double NumericField; @@ -667,6 +674,95 @@ static DataObject createDataObj(Random random) this.ExecuteTestSuite(inputs); } + [TestMethod] + public void TestWeightedRRF() + { + const int Records = 2; + const int MaxStringLength = 100; + static DataObject createDataObj(Random random) + { + DataObject obj = new DataObject + { + StringField = LinqTestsCommon.RandomString(random, random.Next(MaxStringLength)), + IntField = 1, + Id = Guid.NewGuid().ToString(), + Pk = "Test" + }; + return obj; + } + Func> getQuery = LinqTestsCommon.GenerateTestCosmosData(createDataObj, Records, testContainer); + + List inputs = new List + { + // public static double RRF(double[][] scoringFunctions, double[] weights) + new LinqTestInput("Standard weighted RRF calls", b => getQuery(b) + .OrderByRank(doc => RRF(new double[] + { + doc.StringField.FullTextScore(new string[] { "test1" }), + doc.StringField.FullTextScore(new string[] { "test1", "text2" }) + }, + new double[] { 1.0, 2.0 } )) + .Select(doc => doc.Pk)), + + new LinqTestInput("Standard weighted RRF calls using anonymous types", b => getQuery(b) + .OrderByRank(doc => RRF(new [] + { + doc.StringField.FullTextScore(new string[] { "test1" }), + doc.StringField.FullTextScore(new string[] { "test1", "text2" }) + }, + new [] { 1.0, 2.0 } )) + .Select(doc => doc.Pk)), + + // Negative case: weights are not in an array + new LinqTestInput("Weighted RRF with weights and functions not in a list", b => getQuery(b) + .OrderByRank(doc => RRF(doc.StringField.FullTextScore(new string[] { "test1" }), + doc.StringField2.FullTextScore(new string[] { "test1", "test2", "test3" }), + 1.0, + 2.0)) + .Select(doc => doc.Pk)), + new LinqTestInput("Weighted RRF with weights array first", b => getQuery(b) + .OrderByRank(doc => RRF(new double[] { 1.0, 2.0 }, + new double[] + { + doc.StringField.FullTextScore(new string[] { "test1" }), + doc.StringField.FullTextScore(new string[] { "test1", "text2" }) + })) + .Select(doc => doc.Pk)), + new LinqTestInput("Weighted RRF with mixed and matched values/functions in array", b => getQuery(b) + .OrderByRank(doc => RRF(new double[] { + 1.0, + doc.StringField.FullTextScore(new string[] { "test1" }) }, + new double[] + { + 2.0, + doc.StringField.FullTextScore(new string[] { "test1", "text2" }) + })) + .Select(doc => doc.Pk)), + new LinqTestInput("Weighted RRF with mixed and matched values/functions in array 2", b => getQuery(b) + .OrderByRank(doc => RRF(new double[] { + doc.StringField.FullTextScore(new string[] { "test1" }), + doc.StringField.FullTextScore(new string[] { "test1" }) }, + new double[] + { + 2.0, + doc.StringField.FullTextScore(new string[] { "test1", "text2" }) + })) + .Select(doc => doc.Pk)), + + + }; + + foreach (LinqTestInput input in inputs) + { + // OrderBy are not supported client side. + // Therefore this method is verified with baseline only. + input.skipVerification = true; + input.serializeOutput = true; + } + + this.ExecuteTestSuite(inputs); + } + [TestMethod] public void TestOrderByRankFunctionComposeWithOtherFunctions() { @@ -1290,6 +1386,298 @@ public void TestStringFunctions() this.ExecuteTestSuite(inputs); } + [TestMethod] + public void TestArrayContainsAll() + { + const int Records = 10; + const int MaxAbsValue = 10; + const int MaxArraySize = 5; + + int index = 0; + Func createDataObj = (random) => + { + DataObject obj = new DataObject(); + obj.ArrayField = new int[index + 1]; + for (int i = 0; i < obj.ArrayField.Length; ++i) + { + obj.ArrayField[i] = i; + } + obj.EnumerableField = new List(); + for (int i = 0; i < index + MaxArraySize; ++i) + { + obj.EnumerableField.Add(i - MaxArraySize); + } + obj.NumericField = (index * MaxAbsValue * 2) - MaxAbsValue; + obj.Id = GuidFromInt(index).ToString(); + obj.Pk = "Test"; + index++; + return obj; + }; + Func> getQuery = LinqTestsCommon.GenerateTestCosmosData(createDataObj, Records, testContainer); + + List inputs = new List + { + new LinqTestInput("no parameters", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAll()).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with 1 int", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAll(1)).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with 3 ints", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAll(1, 2, 3)).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with 5 ints", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAll(1, 2, 3, int.MaxValue, int.MinValue )).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with mixed types", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAll( + 1, + "hello", + null, + 55f, + 10.123456789d, + new object[] { + "world", + new object[] { "nested array", new { A = int.MaxValue, B = int.MinValue } } })).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("same field", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAll(doc.ArrayField)).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with mixed types", + b => getQuery(b).Where(doc => new object[] { + 1, + "hello", + null, + 55f, + 10.123456789d, + new object[] { + "world", + new object[] { "nested array", new { A = int.MaxValue, B = int.MinValue } } } + }.ArrayContainsAll(doc.ArrayField)).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("use enumerable field", + b => getQuery(b).Where(doc => doc.EnumerableField.ArrayContainsAll(5, 6, 7)).Select(doc => doc.EnumerableField), + skipVerification: true, + serializeOutput: true), + + new LinqTestInput("use document fields", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAll(new object[] { doc.Id, doc.IntField, doc.GuidField })).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with mixed types using document fields", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAll( + 1, + doc.StringField, + null, + doc.VectorFloatField, + 10.123456789d, + new object[] { + "world", + new object[] { doc.ToString(), new { A = doc.StringField2.StartsWith("abc"), B = int.MinValue } } })) + .Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("use document fields", + b => getQuery(b).Where(doc => new object[] { doc.Id, doc.IntField, doc.GuidField }.ArrayContainsAll(doc.Id, doc.IntField, doc.GuidField)).Select(doc => new object[] { doc.Id, doc.IntField, doc.GuidField }), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("use document fields", + b => getQuery(b).Where(doc => new object[] { doc.Id, doc.IntField, doc.GuidField, doc.ArrayField }.ArrayContainsAll(new object[] { doc.ArrayField })).Select(doc => new object[] { doc.Id, doc.IntField, doc.GuidField, doc.ArrayField }), + skipVerification: true, + serializeOutput: true), + + new LinqTestInput("use non-array field", + b => getQuery(b).Where(doc => doc.StringField.ArrayContainsAll(new object[] { doc.Id, doc.IntField, doc.GuidField })).Select(doc => doc.StringField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("use non-array field", + b => getQuery(b).Where(doc => doc.StringField.ArrayContainsAll(doc.VectorFloatField)).Select(doc => doc.StringField), + skipVerification: true, + serializeOutput: true), + + new LinqTestInput("composite condition", + b => getQuery(b).Where(doc => + doc.ArrayField.ArrayContainsAll(new object[] { doc.Id, doc.IntField, doc.GuidField }) || + doc.StringField.StartsWith("abc") && + doc.EnumerableField.ArrayContainsAll(doc.ArrayField)).Select(doc => new object[] { doc.Id, doc.IntField, doc.GuidField, doc.StringField, doc.EnumerableField }), + skipVerification: true, + serializeOutput: true), + + new LinqTestInput("projection", + b => getQuery(b).Select(doc => new object[] { doc.ArrayField, doc.ArrayField.ArrayContainsAll( + 1, + doc.StringField, + null, + doc.VectorFloatField, + 10.123456789d, + new object[] { + "world", + new object[] { doc.ToString(), new { A = doc.StringField2.StartsWith("abc"), B = int.MinValue } } }) }), + skipVerification: true, + serializeOutput: true), + + new LinqTestInput("join", + b => getQuery(b).SelectMany(doc => doc.ArrayField).Select(item => new object[] { item, new[] { item }.ArrayContainsAll(item.ToString()) }), + skipVerification: true, + serializeOutput: true), + }; + + this.ExecuteTestSuite(inputs); + } + + [TestMethod] + public void TestArrayContainsAny() + { + const int Records = 10; + const int MaxAbsValue = 10; + const int MaxArraySize = 5; + + int index = 0; + Func createDataObj = (random) => + { + DataObject obj = new DataObject(); + obj.ArrayField = new int[index + 1]; + for (int i = 0; i < obj.ArrayField.Length; ++i) + { + obj.ArrayField[i] = i; + } + obj.EnumerableField = new List(); + for (int i = 0; i < index + MaxArraySize; ++i) + { + obj.EnumerableField.Add(i - MaxArraySize); + } + obj.NumericField = (index * MaxAbsValue * 2) - MaxAbsValue; + obj.Id = GuidFromInt(index).ToString(); + obj.Pk = "Test"; + index++; + return obj; + }; + Func> getQuery = LinqTestsCommon.GenerateTestCosmosData(createDataObj, Records, testContainer); + + List inputs = new List + { + new LinqTestInput("no parameters", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAny()).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with 1 int", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAny(1)).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with 3 ints", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAny(1, 2, 3)).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with 5 ints", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAny(1, 2, 3, int.MaxValue, int.MinValue )).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with mixed types", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAny( + 1, + "hello", + null, + 55f, + 10.123456789d, + new object[] { + "world", + new object[] { "nested array", new { A = int.MaxValue, B = int.MinValue } } })).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("same field", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAny(doc.ArrayField)).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with mixed types", + b => getQuery(b).Where(doc => new object[] { + 1, + "hello", + null, + 55f, + 10.123456789d, + new object[] { + "world", + new object[] { "nested array", new { A = int.MaxValue, B = int.MinValue } } } + }.ArrayContainsAny(doc.ArrayField)).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("use enumerable field", + b => getQuery(b).Where(doc => doc.EnumerableField.ArrayContainsAny(5, 6, 7, 100)).Select(doc => doc.EnumerableField), + skipVerification: true, + serializeOutput: true), + + new LinqTestInput("use document fields", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAny(new object[] { doc.Id, doc.IntField, doc.GuidField })).Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("params with mixed types using document fields", + b => getQuery(b).Where(doc => doc.ArrayField.ArrayContainsAny( + 1, + doc.StringField, + null, + doc.VectorFloatField, + 10.123456789d, + new object[] { + "world", + new object[] { doc.ToString(), new { A = doc.StringField2.StartsWith("abc"), B = int.MinValue } } })) + .Select(doc => doc.ArrayField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("use document fields", + b => getQuery(b).Where(doc => new object[] { doc.Id, doc.IntField, doc.GuidField }.ArrayContainsAny(doc.Id, doc.IntField, doc.GuidField)).Select(doc => new object[] { doc.Id, doc.IntField, doc.GuidField }), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("use document fields", + b => getQuery(b).Where(doc => new object[] { doc.Id, doc.IntField, doc.GuidField, doc.ArrayField }.ArrayContainsAny(new object[] { doc.ArrayField })).Select(doc => new object[] { doc.Id, doc.IntField, doc.GuidField, doc.ArrayField }), + skipVerification: true, + serializeOutput: true), + + new LinqTestInput("use non-array field", + b => getQuery(b).Where(doc => doc.StringField.ArrayContainsAny(new object[] { doc.Id, doc.IntField, doc.GuidField })).Select(doc => doc.StringField), + skipVerification: true, + serializeOutput: true), + new LinqTestInput("use non-array field", + b => getQuery(b).Where(doc => doc.StringField.ArrayContainsAny(doc.VectorFloatField)).Select(doc => doc.StringField), + skipVerification: true, + serializeOutput: true), + + new LinqTestInput("composite condition", + b => getQuery(b).Where(doc => + doc.ArrayField.ArrayContainsAny(new object[] { doc.Id, doc.IntField, doc.GuidField }) || + doc.StringField.StartsWith("abc") && + doc.EnumerableField.ArrayContainsAny(doc.ArrayField)).Select(doc => new object[] { doc.Id, doc.IntField, doc.GuidField, doc.StringField, doc.EnumerableField }), + skipVerification: true, + serializeOutput: true), + + new LinqTestInput("projection", + b => getQuery(b).Select(doc => new object[] { doc.ArrayField, doc.ArrayField.ArrayContainsAny( + 1, + doc.StringField, + null, + doc.VectorFloatField, + 10.123456789d, + new object[] { + "world", + new object[] { doc.ToString(), new { A = doc.StringField2.StartsWith("abc"), B = int.MinValue } } }) }), + skipVerification: true, + serializeOutput: true), + + new LinqTestInput("join", + b => getQuery(b).SelectMany(doc => doc.ArrayField).Select(item => new object[] { item, new[] { item }.ArrayContainsAny(item.ToString()) }), + skipVerification: true, + serializeOutput: true), + }; + + this.ExecuteTestSuite(inputs); + } + [TestMethod] public void TestArrayFunctions() { @@ -1346,7 +1734,11 @@ public void TestArrayFunctions() new LinqTestInput("Empty list not contains", b => getQuery(b).Select(doc => !emptyList.Contains((int)doc.NumericField))), // Count new LinqTestInput("Count ArrayField", b => getQuery(b).Select(doc => doc.ArrayField.Count())), - new LinqTestInput("Count EnumerableField", b => getQuery(b).Select(doc => doc.EnumerableField.Count())) + new LinqTestInput("Count EnumerableField", b => getQuery(b).Select(doc => doc.EnumerableField.Count())), + + // Unsupported: + // Contains + new LinqTestInput("Contains with EqualityComparer", b => getQuery(b).Select(doc => !doc.EnumerableField.Contains(1, EqualityComparer.Default))), }; this.ExecuteTestSuite(inputs); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/SystemTextJsonSerializer.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/SystemTextJsonSerializer.cs new file mode 100644 index 0000000000..ad121a1a03 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Linq/SystemTextJsonSerializer.cs @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//----------------------------------------------------------------------- +namespace Microsoft.Azure.Cosmos.Services.Management.Tests +{ + using System; + using System.IO; + using System.Text.Json; + using global::Azure.Core.Serialization; + + internal class SystemTextJsonSerializer : CosmosSerializer + { + private readonly JsonObjectSerializer systemTextJsonSerializer; + + public SystemTextJsonSerializer(JsonSerializerOptions jsonSerializerOptions) + { + this.systemTextJsonSerializer = new JsonObjectSerializer(jsonSerializerOptions); + } + + public override T FromStream(Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + using (stream) + { + if (stream.CanSeek && stream.Length == 0) + { + return default; + } + + if (typeof(Stream).IsAssignableFrom(typeof(T))) + { + return (T)(object)stream; + } + + return (T)this.systemTextJsonSerializer.Deserialize(stream, typeof(T), default); + } + } + + public override Stream ToStream(T input) + { + MemoryStream streamPayload = new MemoryStream(); + this.systemTextJsonSerializer.Serialize(streamPayload, input, typeof(T), default); + streamPayload.Position = 0; + return streamPayload; + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Microsoft.Azure.Cosmos.EmulatorTests.csproj b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Microsoft.Azure.Cosmos.EmulatorTests.csproj index 9ad16579cb..3901f5f25f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Microsoft.Azure.Cosmos.EmulatorTests.csproj +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Microsoft.Azure.Cosmos.EmulatorTests.csproj @@ -19,6 +19,9 @@ + + compile + @@ -38,6 +41,8 @@ + + @@ -50,6 +55,7 @@ + @@ -111,6 +117,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -249,6 +261,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -303,9 +318,15 @@ PreserveNewest + + PreserveNewest + PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/AggregateQueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/AggregateQueryTests.cs index 534424f383..753dbac620 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/AggregateQueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/AggregateQueryTests.cs @@ -1,1029 +1,1211 @@ -namespace Microsoft.Azure.Cosmos.EmulatorTests.Query -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Text; - using System.Text.RegularExpressions; - using System.Threading.Tasks; - using System.Xml; - using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.CosmosElements.Numbers; - using Microsoft.Azure.Cosmos.Json; - using Microsoft.Azure.Documents; +namespace Microsoft.Azure.Cosmos.EmulatorTests.Query +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Text; + using System.Text.RegularExpressions; + using System.Threading.Tasks; + using System.Xml; + using Microsoft.Azure.Cosmos.CosmosElements; + using Microsoft.Azure.Cosmos.CosmosElements.Numbers; + using Microsoft.Azure.Cosmos.Json; + using Microsoft.Azure.Documents; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - - [TestClass] - [TestCategory("Query")] - public sealed class AggregateCrossPartitionQueryTests : QueryTestsBase - { - [TestMethod] - public async Task TestAggregateFunctionsAsync() - { - AggregateTestArgs args = new AggregateTestArgs( - numberOfDocumentsDifferentPartitionKey: 43, - numberOfDocsWithSamePartitionKey: 37, - partitionKey: "key", - uniquePartitionKey: "uniquePartitionKey", - field: "field", - values: new List() { false, true, "abc", "cdfg", "opqrs", "ttttttt", "xyz" }); - - List documents = new List(args.NumberOfDocumentsDifferentPartitionKey + args.NumberOfDocsWithSamePartitionKey); - foreach (object val in args.Values) - { - Document doc; - doc = new Document(); - doc.SetPropertyValue(args.PartitionKey, val); - doc.SetPropertyValue("id", Guid.NewGuid().ToString()); - - documents.Add(doc.ToString()); - } - - for (int i = 0; i < args.NumberOfDocsWithSamePartitionKey; ++i) - { - Document doc = new Document(); - doc.SetPropertyValue(args.PartitionKey, args.UniquePartitionKey); - documents.Add(doc.ToString()); - } - - Random random = new Random(); - for (int i = 0; i < args.NumberOfDocumentsDifferentPartitionKey; ++i) - { - Document doc = new Document(); - doc.SetPropertyValue(args.PartitionKey, random.NextDouble()); - documents.Add(doc.ToString()); - } - - await this.CreateIngestQueryDeleteAsync( - ConnectionModes.Direct | ConnectionModes.Gateway, - CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, - documents, - NonOdeImplementationAsync, - args, - "/" + args.PartitionKey); - - await this.CreateIngestQueryDeleteAsync( - ConnectionModes.Direct | ConnectionModes.Gateway, - CollectionTypes.SinglePartition, - documents, - OdeImplementationAsync, - args, - "/" + args.PartitionKey); - - async Task NonOdeImplementationAsync( - Container container, - IReadOnlyList inputDocuments, - AggregateTestArgs aggregateTestArgs) - { - AggregateQueryArguments[] aggregateQueryArgumentsList = CreateAggregateQueryArguments(inputDocuments, aggregateTestArgs); - foreach (int maxDoP in new[] { 0, 10 }) - { - foreach (AggregateQueryArguments argument in aggregateQueryArgumentsList) - { - string[] queryFormats = new[] - { - "SELECT VALUE {0}(r.{1}) FROM r WHERE {2}", - "SELECT VALUE {0}(r.{1}) FROM r WHERE {2} ORDER BY r.{1}" - }; - - foreach (string queryFormat in queryFormats) - { - string query = string.Format( - CultureInfo.InvariantCulture, - queryFormat, - argument.AggregateOperator, - aggregateTestArgs.PartitionKey, - argument.Predicate); - string message = string.Format( - CultureInfo.InvariantCulture, - "query: {0}, data: {1}", - query, - argument.ToString()); - - List items = await QueryTestsBase.RunQueryAsync( - container, - query, - new QueryRequestOptions() - { - MaxConcurrency = maxDoP, - EnableOptimisticDirectExecution = false - }); - - if (argument.ExpectedValue == null) - { - Assert.AreEqual(0, items.Count, message); - } - else - { - Assert.AreEqual(1, items.Count, message); - CosmosElement expected = argument.ExpectedValue; - CosmosElement actual = items.Single(); - - if ((expected is CosmosNumber expectedNumber) && (actual is CosmosNumber actualNumber)) - { - Assert.AreEqual(Number64.ToDouble(expectedNumber.Value), Number64.ToDouble(actualNumber.Value), .01); - } - else - { - if (argument.IgnoreResultOrder) - { - // We need to sort the results for MakeList and MakeSet when comparing because these aggregates don't - // provide a guarantee of the order in which elements appear, and the order can change based on the - // order in which we access the logical partitions. - if ((expected is CosmosArray expectedArray) && (actual is CosmosArray actualArray)) - { - CosmosElement[] normalizedExpected = expectedArray.ToArray(); - Array.Sort(normalizedExpected); - CosmosElement[] normalizedActual = actualArray.ToArray(); - Array.Sort(normalizedActual); - - CollectionAssert.AreEqual(normalizedExpected, normalizedActual); - } - else - { - Assert.AreEqual(expected, actual, message); - } - } - else - { - Assert.AreEqual(expected, actual, message); - } - } - } - } - } - } - } - - async Task OdeImplementationAsync( - Container container, - IReadOnlyList inputDocuments, - AggregateTestArgs aggregateTestArgs) - { - AggregateQueryArguments[] aggregateQueryArgumentsList = CreateAggregateQueryArguments(inputDocuments, aggregateTestArgs); - foreach (int maxDoP in new[] { 0, 10 }) - { - foreach (AggregateQueryArguments argument in aggregateQueryArgumentsList) - { - string[] queryFormats = new[] - { - "SELECT VALUE {0}(r.{1}) FROM r WHERE {2}", - "SELECT VALUE {0}(r.{1}) FROM r WHERE {2} ORDER BY r.{1}" - }; - - foreach (string queryFormat in queryFormats) - { - string query = string.Format( - CultureInfo.InvariantCulture, - queryFormat, - argument.AggregateOperator, - aggregateTestArgs.PartitionKey, - argument.Predicate); - string message = string.Format( - CultureInfo.InvariantCulture, - "query: {0}, data: {1}", - query, - argument.ToString()); - - List items = await QueryTestsBase.RunQueryAsync( - container, - query, - new QueryRequestOptions() - { - MaxConcurrency = maxDoP, - EnableOptimisticDirectExecution = true - }); - - if (argument.ExpectedValue == CosmosUndefined.Create()) - { - Assert.AreEqual(0, items.Count, message); - } - else if (argument.IgnoreResultOrder) - { - // We need to sort the results for MakeList and MakeSet when comparing because these aggregates don't - // provide a guarantee of the order in which elements appear, and the order can change based on the - // order in which we access the logical partitions. - Assert.AreEqual(1, items.Count, message); - CosmosElement expected = argument.ExpectedValue; - CosmosElement actual = items.Single(); - - if ((expected is CosmosArray expectedArray) && (actual is CosmosArray actualArray)) - { - CosmosElement[] normalizedExpected = expectedArray.ToArray(); - Array.Sort(normalizedExpected); - CosmosElement[] normalizedActual = actualArray.ToArray(); - Array.Sort(normalizedActual); - - CollectionAssert.AreEqual(normalizedExpected, normalizedActual); - } - else - { - Assert.AreEqual(expected, actual, message); - } - } - else - { - Assert.AreEqual(1, items.Count, message); - CosmosElement expected = argument.ExpectedValue; - CosmosElement actual = items.Single(); - - if ((expected is CosmosNumber expectedNumber) && (actual is CosmosNumber actualNumber)) - { - Assert.AreEqual(Number64.ToDouble(expectedNumber.Value), Number64.ToDouble(actualNumber.Value), .01); - } - else - { - Assert.AreEqual(expected, actual, message); - } - } - } - } - } + using Newtonsoft.Json.Linq; + + [TestClass] + [TestCategory("Query")] + public sealed class AggregateCrossPartitionQueryTests : QueryTestsBase + { + [TestMethod] + public async Task TestAggregateFunctionsAsync() + { + AggregateTestArgs args = new AggregateTestArgs( + numberOfDocumentsDifferentPartitionKey: 43, + numberOfDocsWithSamePartitionKey: 37, + partitionKey: "key", + uniquePartitionKey: "uniquePartitionKey", + field: "field", + values: new List() { false, true, "abc", "cdfg", "opqrs", "ttttttt", "xyz" }); + + List documents = new List(args.NumberOfDocumentsDifferentPartitionKey + args.NumberOfDocsWithSamePartitionKey); + foreach (object val in args.Values) + { + Document doc; + doc = new Document(); + doc.SetPropertyValue(args.PartitionKey, val); + doc.SetPropertyValue("id", Guid.NewGuid().ToString()); + + documents.Add(doc.ToString()); } + for (int i = 0; i < args.NumberOfDocsWithSamePartitionKey; ++i) + { + Document doc = new Document(); + doc.SetPropertyValue(args.PartitionKey, args.UniquePartitionKey); + documents.Add(doc.ToString()); + } - static AggregateQueryArguments[] CreateAggregateQueryArguments( + Random random = new Random(); + for (int i = 0; i < args.NumberOfDocumentsDifferentPartitionKey; ++i) + { + Document doc = new Document(); + doc.SetPropertyValue(args.PartitionKey, random.NextDouble()); + documents.Add(doc.ToString()); + } + + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct | ConnectionModes.Gateway, + CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, + documents, + NonOdeImplementationAsync, + args, + "/" + args.PartitionKey); + + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct | ConnectionModes.Gateway, + CollectionTypes.SinglePartition, + documents, + OdeImplementationAsync, + args, + "/" + args.PartitionKey); + + async Task NonOdeImplementationAsync( + Container container, IReadOnlyList inputDocuments, AggregateTestArgs aggregateTestArgs) { - IReadOnlyList documentsWherePkIsANumber = inputDocuments - .Where(doc => + AggregateQueryArguments[] aggregateQueryArgumentsList = CreateAggregateQueryArguments(inputDocuments, aggregateTestArgs); + foreach (int maxDoP in new[] { 0, 10 }) + { + foreach (AggregateQueryArguments argument in aggregateQueryArgumentsList) + { + string[] queryFormats = new[] { - return double.TryParse( - doc[aggregateTestArgs.PartitionKey].ToString(), - out double result); - }) - .ToList(); - double numberSum = documentsWherePkIsANumber - .Sum(doc => + "SELECT VALUE {0}(r.{1}) FROM r WHERE {2}", + "SELECT VALUE {0}(r.{1}) FROM r WHERE {2} ORDER BY r.{1}" + }; + + foreach (string queryFormat in queryFormats) + { + string query = string.Format( + CultureInfo.InvariantCulture, + queryFormat, + argument.AggregateOperator, + aggregateTestArgs.PartitionKey, + argument.Predicate); + string message = string.Format( + CultureInfo.InvariantCulture, + "query: {0}, data: {1}", + query, + argument.ToString()); + + List items = await QueryTestsBase.RunQueryAsync( + container, + query, + new QueryRequestOptions() + { + MaxConcurrency = maxDoP, + EnableOptimisticDirectExecution = false + }); + + if (argument.ExpectedValue == null) + { + Assert.AreEqual(0, items.Count, message); + } + else + { + Assert.AreEqual(1, items.Count, message); + CosmosElement expected = argument.ExpectedValue; + CosmosElement actual = items.Single(); + + if ((expected is CosmosNumber expectedNumber) && (actual is CosmosNumber actualNumber)) + { + Assert.AreEqual(Number64.ToDouble(expectedNumber.Value), Number64.ToDouble(actualNumber.Value), .01); + } + else + { + if (argument.IgnoreResultOrder) + { + // We need to sort the results for MakeList and MakeSet when comparing because these aggregates don't + // provide a guarantee of the order in which elements appear, and the order can change based on the + // order in which we access the logical partitions. + if ((expected is CosmosArray expectedArray) && (actual is CosmosArray actualArray)) + { + CosmosElement[] normalizedExpected = expectedArray.ToArray(); + Array.Sort(normalizedExpected); + CosmosElement[] normalizedActual = actualArray.ToArray(); + Array.Sort(normalizedActual); + + CollectionAssert.AreEqual(normalizedExpected, normalizedActual); + } + else + { + Assert.AreEqual(expected, actual, message); + } + } + else + { + Assert.AreEqual(expected, actual, message); + } + } + } + } + } + } + } + + async Task OdeImplementationAsync( + Container container, + IReadOnlyList inputDocuments, + AggregateTestArgs aggregateTestArgs) + { + AggregateQueryArguments[] aggregateQueryArgumentsList = CreateAggregateQueryArguments(inputDocuments, aggregateTestArgs); + foreach (int maxDoP in new[] { 0, 10 }) + { + foreach (AggregateQueryArguments argument in aggregateQueryArgumentsList) { - if (!doc.TryGetValue(aggregateTestArgs.PartitionKey, out CosmosNumber number)) + string[] queryFormats = new[] { - Assert.Fail("Failed to get partition key from document"); + "SELECT VALUE {0}(r.{1}) FROM r WHERE {2}", + "SELECT VALUE {0}(r.{1}) FROM r WHERE {2} ORDER BY r.{1}" + }; + + foreach (string queryFormat in queryFormats) + { + string query = string.Format( + CultureInfo.InvariantCulture, + queryFormat, + argument.AggregateOperator, + aggregateTestArgs.PartitionKey, + argument.Predicate); + string message = string.Format( + CultureInfo.InvariantCulture, + "query: {0}, data: {1}", + query, + argument.ToString()); + + List items = await QueryTestsBase.RunQueryAsync( + container, + query, + new QueryRequestOptions() + { + MaxConcurrency = maxDoP, + EnableOptimisticDirectExecution = true + }); + + if (argument.ExpectedValue == CosmosUndefined.Create()) + { + Assert.AreEqual(0, items.Count, message); + } + else if (argument.IgnoreResultOrder) + { + // We need to sort the results for MakeList and MakeSet when comparing because these aggregates don't + // provide a guarantee of the order in which elements appear, and the order can change based on the + // order in which we access the logical partitions. + Assert.AreEqual(1, items.Count, message); + CosmosElement expected = argument.ExpectedValue; + CosmosElement actual = items.Single(); + + if ((expected is CosmosArray expectedArray) && (actual is CosmosArray actualArray)) + { + CosmosElement[] normalizedExpected = expectedArray.ToArray(); + Array.Sort(normalizedExpected); + CosmosElement[] normalizedActual = actualArray.ToArray(); + Array.Sort(normalizedActual); + + CollectionAssert.AreEqual(normalizedExpected, normalizedActual); + } + else + { + Assert.AreEqual(expected, actual, message); + } + } + else + { + Assert.AreEqual(1, items.Count, message); + CosmosElement expected = argument.ExpectedValue; + CosmosElement actual = items.Single(); + + if ((expected is CosmosNumber expectedNumber) && (actual is CosmosNumber actualNumber)) + { + Assert.AreEqual(Number64.ToDouble(expectedNumber.Value), Number64.ToDouble(actualNumber.Value), .01); + } + else + { + Assert.AreEqual(expected, actual, message); + } + } } + } + } + } + } + + private static AggregateQueryArguments[] CreateAggregateQueryArguments( + IReadOnlyList inputDocuments, + AggregateTestArgs aggregateTestArgs) + { + IReadOnlyList documentsWherePkIsANumber = inputDocuments + .Where(doc => + { + return double.TryParse( + doc[aggregateTestArgs.PartitionKey].ToString(), + out double result); + }) + .ToList(); + double numberSum = documentsWherePkIsANumber + .Sum(doc => + { + if (!doc.TryGetValue(aggregateTestArgs.PartitionKey, out CosmosNumber number)) + { + Assert.Fail("Failed to get partition key from document"); + } + + return Number64.ToDouble(number.Value); + }); + double count = documentsWherePkIsANumber.Count(); + + IReadOnlyList makeListResult = inputDocuments + .Select(doc => + { + if (!doc.TryGetValue(aggregateTestArgs.PartitionKey, out CosmosElement cosmosElement)) + { + Assert.Fail("Failed to get partition key from document"); + } - return Number64.ToDouble(number.Value); - }); - double count = documentsWherePkIsANumber.Count(); - AggregateQueryArguments[] aggregateQueryArgumentsList = new AggregateQueryArguments[] - { - new AggregateQueryArguments( - aggregateOperator: "AVG", - expectedValue: CosmosNumber64.Create(numberSum / count), - predicate: $"IS_NUMBER(r.{aggregateTestArgs.PartitionKey})"), - new AggregateQueryArguments( - aggregateOperator: "AVG", - expectedValue: CosmosUndefined.Create(), - predicate: "true"), - new AggregateQueryArguments( - aggregateOperator: "COUNT", - expectedValue: CosmosNumber64.Create(inputDocuments.Count()), - predicate: "true"), - new AggregateQueryArguments( - aggregateOperator: "MAX", - expectedValue: CosmosString.Create("xyz"), - predicate: "true"), - new AggregateQueryArguments( - aggregateOperator: "MIN", - expectedValue: CosmosBoolean.Create(false), - predicate: "true"), - new AggregateQueryArguments( - aggregateOperator: "SUM", - expectedValue: CosmosNumber64.Create(numberSum), - predicate: $"IS_NUMBER(r.{aggregateTestArgs.PartitionKey})"), - new AggregateQueryArguments( - aggregateOperator: "SUM", - expectedValue: CosmosUndefined.Create(), + return cosmosElement; + }) + .ToList(); + + IReadOnlyList makeSetResult = makeListResult.Distinct().ToList(); + + AggregateQueryArguments[] aggregateQueryArgumentsList = new AggregateQueryArguments[] + { + new AggregateQueryArguments( + aggregateOperator: "AVG", + expectedValue: CosmosNumber64.Create(numberSum / count), + predicate: $"IS_NUMBER(r.{aggregateTestArgs.PartitionKey})"), + new AggregateQueryArguments( + aggregateOperator: "AVG", + expectedValue: CosmosUndefined.Create(), + predicate: "true"), + new AggregateQueryArguments( + aggregateOperator: "COUNT", + expectedValue: CosmosNumber64.Create(inputDocuments.Count()), + predicate: "true"), + new AggregateQueryArguments( + aggregateOperator: "MAKELIST", + expectedValue: CosmosArray.Create(makeListResult), + predicate: "true", + ignoreResultOrder: true), + new AggregateQueryArguments( + aggregateOperator: "MAKESET", + expectedValue: CosmosArray.Create(makeSetResult), + predicate: "true", + ignoreResultOrder: true), + new AggregateQueryArguments( + aggregateOperator: "MAX", + expectedValue: CosmosString.Create("xyz"), + predicate: "true"), + new AggregateQueryArguments( + aggregateOperator: "MIN", + expectedValue: CosmosBoolean.Create(false), + predicate: "true"), + new AggregateQueryArguments( + aggregateOperator: "SUM", + expectedValue: CosmosNumber64.Create(numberSum), + predicate: $"IS_NUMBER(r.{aggregateTestArgs.PartitionKey})"), + new AggregateQueryArguments( + aggregateOperator: "SUM", + expectedValue: CosmosUndefined.Create(), predicate: $"true"), + }; + + return aggregateQueryArgumentsList; + } + + private readonly struct AggregateTestArgs + { + public AggregateTestArgs( + int numberOfDocumentsDifferentPartitionKey, + int numberOfDocsWithSamePartitionKey, + string partitionKey, + string uniquePartitionKey, + string field, + IReadOnlyList values) + { + this.NumberOfDocumentsDifferentPartitionKey = numberOfDocumentsDifferentPartitionKey; + this.NumberOfDocsWithSamePartitionKey = numberOfDocsWithSamePartitionKey; + this.PartitionKey = partitionKey; + this.UniquePartitionKey = uniquePartitionKey; + this.Field = field; + this.Values = values; + } + + public int NumberOfDocumentsDifferentPartitionKey { get; } + public int NumberOfDocsWithSamePartitionKey { get; } + public string PartitionKey { get; } + public string UniquePartitionKey { get; } + public string Field { get; } + public IReadOnlyList Values { get; } + } + + private readonly struct AggregateQueryArguments + { + public AggregateQueryArguments(string aggregateOperator, CosmosElement expectedValue, string predicate, bool ignoreResultOrder=false) + { + this.AggregateOperator = aggregateOperator; + this.ExpectedValue = expectedValue; + this.Predicate = predicate; + this.IgnoreResultOrder = ignoreResultOrder; + } + + public string AggregateOperator { get; } + public CosmosElement ExpectedValue { get; } + public string Predicate { get; } + public bool IgnoreResultOrder { get; } + + public override string ToString() + { + IJsonWriter writer = Cosmos.Json.JsonWriter.Create(JsonSerializationFormat.Text); + writer.WriteObjectStart(); + + writer.WriteFieldName(nameof(this.AggregateOperator)); + writer.WriteStringValue(this.AggregateOperator); + + writer.WriteFieldName(nameof(this.ExpectedValue)); + if (this.ExpectedValue is not CosmosUndefined) + { + writer.WriteStringValue(this.ExpectedValue.ToString()); + } + else + { + writer.WriteObjectStart(); + writer.WriteObjectEnd(); + } + + writer.WriteFieldName(nameof(this.Predicate)); + writer.WriteStringValue(this.Predicate); + + writer.WriteObjectEnd(); + + return Utf8StringHelpers.ToString(writer.GetResult()); + } + } + + [TestMethod] + public async Task TestAggregateFunctionsWithEmptyPartitionsAsync() + { + AggregateQueryEmptyPartitionsArgs testArgs = new AggregateQueryEmptyPartitionsArgs() + { + NumDocuments = 100, + PartitionKey = "key", + UniqueField = "UniqueField", + }; + + List inputDocuments = new List(testArgs.NumDocuments); + for (int i = 0; i < testArgs.NumDocuments; ++i) + { + Document doc = new Document(); + doc.SetPropertyValue(testArgs.PartitionKey, Guid.NewGuid()); + doc.SetPropertyValue(testArgs.UniqueField, i); + inputDocuments.Add(doc.ToString()); + } + + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct | ConnectionModes.Gateway, + CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, + inputDocuments, + ImplementationAsync, + testArgs, + "/" + testArgs.PartitionKey); + + async Task ImplementationAsync( + Container container, + IReadOnlyList documents, + AggregateQueryEmptyPartitionsArgs args) + { + int numDocuments = args.NumDocuments; + string partitionKey = args.PartitionKey; + string uniqueField = args.UniqueField; + + // Perform full fanouts but only match a single value that isn't the partition key. + // This leads to all other partitions returning { "" = UNDEFINDED, "count" = 0 } + // which should be ignored from the aggregation. + int valueOfInterest = args.NumDocuments / 2; + string[] queries = new string[] + { + $"SELECT VALUE AVG(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", + $"SELECT VALUE MIN(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", + $"SELECT VALUE MAX(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", + $"SELECT VALUE SUM(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", }; - return aggregateQueryArgumentsList; + foreach (string query in queries) + { + try + { + List items = await QueryTestsBase.RunQueryAsync( + container, + query, + new QueryRequestOptions() + { + MaxConcurrency = 10, + }); + + Assert.AreEqual(valueOfInterest, items.Single().ToDouble()); + } + catch (Exception ex) + { + Assert.Fail($"Something went wrong with query: {query}, ex: {ex}"); + } + } + + string[] arrayAggregateQueries = new string[] + { + $"SELECT VALUE MAKELIST(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", + $"SELECT VALUE MAKESET(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", + }; + + foreach (string query in arrayAggregateQueries) + { + try + { + List items = await QueryTestsBase.RunQueryAsync( + container, + query, + new QueryRequestOptions() + { + MaxConcurrency = 10, + }); + + Assert.IsTrue((items.Count() == 1) && (items.Single() is CosmosArray result) && result.Equals(CosmosArray.Create(CosmosNumber64.Create(valueOfInterest)))); + } + catch (Exception ex) + { + Assert.Fail($"Something went wrong with query: {query}, ex: {ex}"); + } + } + } + } + + private struct AggregateQueryEmptyPartitionsArgs + { + public int NumDocuments; + public string PartitionKey; + public string UniqueField; + } + + [TestMethod] + public async Task TestAggregateFunctionsWithMixedTypesAsync() + { + AggregateQueryMixedTypes args = new AggregateQueryMixedTypes() + { + PartitionKey = "key", + Field = "field", + DoubleOnlyKey = "doubleOnly", + StringOnlyKey = "stringOnly", + BoolOnlyKey = "boolOnly", + NullOnlyKey = "nullOnly", + ObjectOnlyKey = "objectOnlyKey", + ArrayOnlyKey = "arrayOnlyKey", + OneObjectKey = "oneObjectKey", + OneArrayKey = "oneArrayKey", + UndefinedKey = "undefinedKey", + }; + + List documents = new List(); + Random random = new Random(1234); + for (int i = 0; i < 20; ++i) + { + Document doubleDoc = new Document(); + doubleDoc.SetPropertyValue(args.PartitionKey, Guid.NewGuid()); + doubleDoc.SetPropertyValue(args.Field, random.Next(1, 100000)); + documents.Add(doubleDoc.ToString()); + doubleDoc.SetPropertyValue(args.PartitionKey, args.DoubleOnlyKey); + documents.Add(doubleDoc.ToString()); + + Document stringDoc = new Document(); + stringDoc.SetPropertyValue(args.PartitionKey, Guid.NewGuid()); + stringDoc.SetPropertyValue(args.Field, random.NextDouble().ToString()); + documents.Add(stringDoc.ToString()); + stringDoc.SetPropertyValue(args.PartitionKey, args.StringOnlyKey); + documents.Add(stringDoc.ToString()); + + Document boolDoc = new Document(); + boolDoc.SetPropertyValue(args.PartitionKey, Guid.NewGuid()); + boolDoc.SetPropertyValue(args.Field, random.Next() % 2 == 0); + documents.Add(boolDoc.ToString()); + boolDoc.SetPropertyValue(args.PartitionKey, args.BoolOnlyKey); + documents.Add(boolDoc.ToString()); + + Document nullDoc = new Document(); + nullDoc.SetPropertyValue(args.PartitionKey, Guid.NewGuid()); + nullDoc.propertyBag.Add(args.Field, null); + documents.Add(nullDoc.ToString()); + nullDoc.SetPropertyValue(args.PartitionKey, args.NullOnlyKey); + documents.Add(nullDoc.ToString()); + + Document objectDoc = new Document(); + objectDoc.SetPropertyValue(args.PartitionKey, Guid.NewGuid()); + objectDoc.SetPropertyValue(args.Field, new object { }); + documents.Add(objectDoc.ToString()); + objectDoc.SetPropertyValue(args.PartitionKey, args.ObjectOnlyKey); + documents.Add(objectDoc.ToString()); + + Document arrayDoc = new Document(); + arrayDoc.SetPropertyValue(args.PartitionKey, Guid.NewGuid()); + arrayDoc.SetPropertyValue(args.Field, new object[] { }); + documents.Add(arrayDoc.ToString()); + arrayDoc.SetPropertyValue(args.PartitionKey, args.ArrayOnlyKey); + documents.Add(arrayDoc.ToString()); + } + + Document oneObjectDoc = new Document(); + oneObjectDoc.SetPropertyValue(args.PartitionKey, args.OneObjectKey); + oneObjectDoc.SetPropertyValue(args.Field, new object { }); + documents.Add(oneObjectDoc.ToString()); + + Document oneArrayDoc = new Document(); + oneArrayDoc.SetPropertyValue(args.PartitionKey, args.OneArrayKey); + oneArrayDoc.SetPropertyValue(args.Field, new object[] { }); + documents.Add(oneArrayDoc.ToString()); + + Document undefinedDoc = new Document(); + undefinedDoc.SetPropertyValue(args.PartitionKey, args.UndefinedKey); + // This doc does not have the field key set + documents.Add(undefinedDoc.ToString()); + + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct | ConnectionModes.Gateway, + CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, + documents, + this.TestQueryCrossPartitionAggregateFunctionsWithMixedTypesHelper, + args, + "/" + args.PartitionKey); + } + + private struct AggregateQueryMixedTypes + { + public string PartitionKey; + public string Field; + public string DoubleOnlyKey; + public string StringOnlyKey; + public string BoolOnlyKey; + public string NullOnlyKey; + public string ObjectOnlyKey; + public string ArrayOnlyKey; + public string OneObjectKey; + public string OneArrayKey; + public string UndefinedKey; + } + + private async Task TestQueryCrossPartitionAggregateFunctionsWithMixedTypesHelper( + Container container, + IReadOnlyList documents, + AggregateQueryMixedTypes args) + { + await QueryTestsBase.NoOp(); + string partitionKey = args.PartitionKey; + string field = args.Field; + string[] typeOnlyPartitionKeys = new string[] + { + args.DoubleOnlyKey, + args.StringOnlyKey, + args.BoolOnlyKey, + args.NullOnlyKey, + args.ObjectOnlyKey, + args.ArrayOnlyKey, + args.OneArrayKey, + args.OneObjectKey, + args.UndefinedKey + }; + + string[] aggregateOperators = new string[] { "AVG", "MIN", "MAX", "SUM", "COUNT", "MAKELIST", "MAKESET" }; + string[] typeCheckFunctions = new string[] { "IS_ARRAY", "IS_BOOL", "IS_NULL", "IS_NUMBER", "IS_OBJECT", "IS_STRING", "IS_DEFINED", "IS_PRIMITIVE" }; + List<(string, bool)> queries = new List<(string, bool)>(); + foreach (string aggregateOperator in aggregateOperators) + { + bool ignoreResultOrder = aggregateOperator.Equals("MAKELIST") || aggregateOperator.Equals("MAKESET"); + foreach (string typeCheckFunction in typeCheckFunctions) + { + queries.Add( + ($@" + SELECT VALUE {aggregateOperator} (c.{field}) + FROM c + WHERE {typeCheckFunction}(c.{field}) + ", + ignoreResultOrder)); + } + + foreach (string typeOnlyPartitionKey in typeOnlyPartitionKeys) + { + queries.Add( + ($@" + SELECT VALUE {aggregateOperator} (c.{field}) + FROM c + WHERE c.{partitionKey} = ""{typeOnlyPartitionKey}"" + ", + ignoreResultOrder)); + } + }; + + // mixing primitive and non primitives + foreach (string minmaxop in new string[] { "MIN", "MAX" }) + { + bool ignoreResultOrder = false; + foreach (string key in new string[] { args.OneObjectKey, args.OneArrayKey }) + { + queries.Add( + ($@" + SELECT VALUE {minmaxop} (c.{field}) + FROM c + WHERE c.{partitionKey} IN (""{key}"", ""{args.DoubleOnlyKey}"") + ", + ignoreResultOrder)); + } + } + + string filename = $"Query/AggregateQueryTests.AggregateMixedTypes"; + string baselinePath = $"{filename}_baseline.xml"; + + XmlWriterSettings settings = new XmlWriterSettings() + { + OmitXmlDeclaration = true, + Indent = true, + NewLineOnAttributes = true, + }; + + StringBuilder builder = new StringBuilder(); + using (XmlWriter writer = XmlWriter.Create(builder, settings)) + { + writer.WriteStartDocument(); + writer.WriteStartElement("Results"); + foreach ( (string query, bool ignoreResultOrder) in queries) + { + string formattedQuery = string.Join( + Environment.NewLine, + query.Trim().Split( + new[] { Environment.NewLine }, + StringSplitOptions.None) + .Select(x => x.Trim())); + + List items = await QueryTestsBase.RunQueryAsync( + container, + query, + new QueryRequestOptions() + { + MaxItemCount = 10, + }); + + writer.WriteStartElement("Result"); + writer.WriteStartElement("Query"); + writer.WriteCData(formattedQuery); + writer.WriteEndElement(); + writer.WriteStartElement("Aggregation"); + + if (items.Count > 0) + { + Assert.AreEqual(1, items.Count); + CosmosElement aggregateResult = items.First(); + if(aggregateResult is not CosmosUndefined) + { + if (ignoreResultOrder && (aggregateResult is CosmosArray aggregateResultArray)) + { + CosmosElement[] normalizedAggregateResult = aggregateResultArray.ToArray(); + Array.Sort(normalizedAggregateResult); + writer.WriteCData(CosmosArray.Create(normalizedAggregateResult).ToString()); + } + else + { + writer.WriteCData(items.Single().ToString()); + } + } + } + + writer.WriteEndElement(); + writer.WriteEndElement(); + } + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + + Regex r = new Regex(">\\s+"); + string normalizedBaseline = r.Replace(File.ReadAllText(baselinePath), ">"); + string normalizedOutput = r.Replace(builder.ToString(), ">"); + + Assert.AreEqual(normalizedBaseline, normalizedOutput); + } + + [TestMethod] + [Owner("brchon")] + public async Task TestNonValueAggregatesAsync() + { + string[] documents = new string[] + { + @"{""first"":""Good"",""last"":""Trevino"",""age"":23,""height"":61,""income"":59848}", + @"{""first"":""Charles"",""last"":""Decker"",""age"":31,""height"":64,""income"":55970}", + @"{""first"":""Holden"",""last"":""Cotton"",""age"":30,""height"":66,""income"":57075}", + @"{""first"":""Carlene"",""last"":""Cabrera"",""age"":26,""height"":72,""income"":98018}", + @"{""first"":""Gates"",""last"":""Spence"",""age"":38,""height"":53,""income"":12338}", + @"{""first"":""Camacho"",""last"":""Singleton"",""age"":40,""height"":52,""income"":76973}", + @"{""first"":""Rachel"",""last"":""Tucker"",""age"":27,""height"":68,""income"":28116}", + @"{""first"":""Kristi"",""last"":""Robertson"",""age"":32,""height"":53,""income"":61687}", + @"{""first"":""Poole"",""last"":""Petty"",""age"":22,""height"":75,""income"":53381}", + @"{""first"":""Lacey"",""last"":""Carlson"",""age"":38,""height"":78,""income"":63989}", + @"{""first"":""Rosario"",""last"":""Mendez"",""age"":21,""height"":64,""income"":20300}", + @"{""first"":""Estrada"",""last"":""Collins"",""age"":28,""height"":74,""income"":6926}", + @"{""first"":""Ursula"",""last"":""Burton"",""age"":26,""height"":66,""income"":32870}", + @"{""first"":""Rochelle"",""last"":""Sanders"",""age"":24,""height"":56,""income"":47564}", + @"{""first"":""Darcy"",""last"":""Herring"",""age"":27,""height"":52,""income"":67436}", + @"{""first"":""Carole"",""last"":""Booth"",""age"":34,""height"":60,""income"":50177}", + @"{""first"":""Cruz"",""last"":""Russell"",""age"":25,""height"":52,""income"":95072}", + @"{""first"":""Wilma"",""last"":""Robbins"",""age"":36,""height"":50,""income"":53008}", + @"{""first"":""Mcdaniel"",""last"":""Barlow"",""age"":21,""height"":78,""income"":85441}", + @"{""first"":""Leann"",""last"":""Blackwell"",""age"":40,""height"":79,""income"":900}", + @"{""first"":""Hoffman"",""last"":""Hoffman"",""age"":31,""height"":76,""income"":1208}", + @"{""first"":""Pittman"",""last"":""Shepherd"",""age"":35,""height"":61,""income"":26887}", + @"{""first"":""Wright"",""last"":""Rojas"",""age"":35,""height"":73,""income"":76487}", + @"{""first"":""Lynne"",""last"":""Waters"",""age"":27,""height"":60,""income"":22926}", + @"{""first"":""Corina"",""last"":""Shelton"",""age"":29,""height"":78,""income"":67379}", + @"{""first"":""Alvarez"",""last"":""Barr"",""age"":29,""height"":59,""income"":34698}", + @"{""first"":""Melinda"",""last"":""Mccoy"",""age"":24,""height"":63,""income"":69811}", + @"{""first"":""Chelsea"",""last"":""Bolton"",""age"":20,""height"":63,""income"":47698}", + @"{""first"":""English"",""last"":""Ingram"",""age"":28,""height"":50,""income"":94977}", + @"{""first"":""Vance"",""last"":""Thomas"",""age"":30,""height"":49,""income"":67638}", + @"{""first"":""Howell"",""last"":""Joyner"",""age"":34,""height"":78,""income"":65547}", + @"{""first"":""Ofelia"",""last"":""Chapman"",""age"":23,""height"":82,""income"":85049}", + @"{""first"":""Downs"",""last"":""Adams"",""age"":28,""height"":76,""income"":19373}", + @"{""first"":""Terrie"",""last"":""Bryant"",""age"":32,""height"":55,""income"":79024}", + @"{""first"":""Jeanie"",""last"":""Carson"",""age"":26,""height"":52,""income"":68293}", + @"{""first"":""Hazel"",""last"":""Bean"",""age"":40,""height"":70,""income"":46028}", + @"{""first"":""Dominique"",""last"":""Norman"",""age"":25,""height"":50,""income"":59445}", + @"{""first"":""Lyons"",""last"":""Patterson"",""age"":36,""height"":64,""income"":71748}", + @"{""first"":""Catalina"",""last"":""Cantrell"",""age"":30,""height"":78,""income"":16999}", + @"{""first"":""Craft"",""last"":""Head"",""age"":30,""height"":49,""income"":10542}", + @"{""first"":""Suzanne"",""last"":""Gilliam"",""age"":36,""height"":77,""income"":7511}", + @"{""first"":""Pamela"",""last"":""Merritt"",""age"":30,""height"":81,""income"":80653}", + @"{""first"":""Haynes"",""last"":""Ayala"",""age"":38,""height"":65,""income"":85832}", + @"{""first"":""Teri"",""last"":""Martin"",""age"":40,""height"":83,""income"":27839}", + @"{""first"":""Susanne"",""last"":""Short"",""age"":25,""height"":57,""income"":48957}", + @"{""first"":""Rosalie"",""last"":""Camacho"",""age"":24,""height"":83,""income"":30313}", + @"{""first"":""Walls"",""last"":""Bray"",""age"":28,""height"":74,""income"":21616}", + @"{""first"":""Norris"",""last"":""Bates"",""age"":23,""height"":59,""income"":13631}", + @"{""first"":""Wendy"",""last"":""King"",""age"":38,""height"":48,""income"":19845}", + @"{""first"":""Deena"",""last"":""Ramsey"",""age"":20,""height"":66,""income"":49665}", + @"{""first"":""Richmond"",""last"":""Meadows"",""age"":36,""height"":59,""income"":43244}", + @"{""first"":""Burks"",""last"":""Whitley"",""age"":25,""height"":55,""income"":39974}", + @"{""first"":""Gilliam"",""last"":""George"",""age"":37,""height"":82,""income"":47114}", + @"{""first"":""Marcy"",""last"":""Harding"",""age"":33,""height"":80,""income"":20316}", + @"{""first"":""Curtis"",""last"":""Gomez"",""age"":31,""height"":50,""income"":69085}", + @"{""first"":""Lopez"",""last"":""Burt"",""age"":34,""height"":79,""income"":37577}", + @"{""first"":""Nell"",""last"":""Nixon"",""age"":37,""height"":58,""income"":67999}", + @"{""first"":""Sonja"",""last"":""Lamb"",""age"":37,""height"":53,""income"":92553}", + @"{""first"":""Owens"",""last"":""Fischer"",""age"":40,""height"":48,""income"":75199}", + @"{""first"":""Ortega"",""last"":""Padilla"",""age"":28,""height"":55,""income"":29126}", + @"{""first"":""Stacie"",""last"":""Velez"",""age"":20,""height"":56,""income"":45292}", + @"{""first"":""Brennan"",""last"":""Craig"",""age"":38,""height"":65,""income"":37445}" + }; + + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct | ConnectionModes.Gateway, + /*CollectionTypes.SinglePartition |*/ CollectionTypes.MultiPartition, + documents, + this.TestNonValueAggregates); + } + + private async Task TestNonValueAggregates( + Container container, + IReadOnlyList documents) + { + // ------------------------------------------ + // Positive + // ------------------------------------------ + + List<(string, CosmosElement)> queryAndExpectedAggregation = new List<(string, CosmosElement)>() + { + // ------------------------------------------ + // Simple Aggregates without a value + // ------------------------------------------ + + ( + "SELECT SUM(c.age) FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "$1", + CosmosNumber64.Create( + documents.Sum(document => document["age"].ToDouble())) + } + }) + ), + + ( + "SELECT COUNT(c.age) FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "$1", + CosmosNumber64.Create( + documents.Where(document => document.TryGetValue("age", out _)).Count()) + } + }) + ), + + ( + "SELECT MIN(c.age) FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "$1", + CosmosNumber64.Create( + documents.Min(document => document["age"].ToDouble())) + } + }) + ), + + ( + "SELECT MAX(c.age) FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "$1", + CosmosNumber64.Create( + documents.Max(document => document["age"].ToDouble())) + } + }) + ), + + ( + "SELECT AVG(c.age) FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "$1", + CosmosNumber64.Create( + documents.Average(document => document["age"].ToDouble())) + } + }) + ), + + // ------------------------------------------ + // Simple aggregates with alias + // ------------------------------------------ + + ( + "SELECT SUM(c.age) as sum_age FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "sum_age", + CosmosNumber64.Create( + documents.Sum(document => document["age"].ToDouble())) + } + }) + ), + + ( + "SELECT COUNT(c.age) as count_age FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "count_age", + CosmosNumber64.Create( + documents.Where(document => document.TryGetValue("age", out _)).Count()) + } + }) + ), + + ( + "SELECT MIN(c.age) as min_age FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "min_age", + CosmosNumber64.Create( + documents.Min(document => document["age"].ToDouble())) + } + }) + ), + + ( + "SELECT MAX(c.age) as max_age FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "max_age", + CosmosNumber64.Create( + documents.Max(document => document["age"].ToDouble())) + } + }) + ), + + ( + "SELECT AVG(c.age) as avg_age FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "avg_age", + CosmosNumber64.Create( + documents.Average(document => document["age"].ToDouble())) + } + }) + ), + + // ------------------------------------------ + // Multiple Aggregates without alias + // ------------------------------------------ + + ( + "SELECT MIN(c.age), MAX(c.age) FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "$1", + CosmosNumber64.Create( + documents.Min(document => document["age"].ToDouble())) + }, + { + "$2", + CosmosNumber64.Create( + documents.Max(document => document["age"].ToDouble())) + }, + }) + ), + + // ------------------------------------------ + // Multiple Aggregates with alias + // ------------------------------------------ + + ( + "SELECT MIN(c.age) as min_age, MAX(c.age) as max_age FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "min_age", + CosmosNumber64.Create( + documents.Min(document => document["age"].ToDouble())) + }, + { + "max_age", + CosmosNumber64.Create( + documents.Max(document => document["age"].ToDouble())) + }, + }) + ), + + // ------------------------------------------ + // Multiple Aggregates with and without alias + // ------------------------------------------ + + ( + "SELECT MIN(c.age), MAX(c.age) as max_age FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "$1", + CosmosNumber64.Create( + documents.Min(document => document["age"].ToDouble())) + }, + { + "max_age", + CosmosNumber64.Create( + documents.Max(document => document["age"].ToDouble())) + }, + }) + ), + + ( + "SELECT MIN(c.age) as min_age, MAX(c.age) FROM c", + CosmosObject.Create( + new Dictionary() + { + { + "min_age", + CosmosNumber64.Create( + documents.Min(document => document["age"].ToDouble())) + }, + { + "$1", + CosmosNumber64.Create( + documents.Max(document => document["age"].ToDouble())) + }, + }) + ), + }; + + // Test query correctness. + foreach ((string query, CosmosElement expectedAggregation) in queryAndExpectedAggregation) + { + foreach (int maxItemCount in new int[] { 1, 5, 10 }) + { + List actualAggregationQuery = await RunQueryAsync( + container: container, + query: query, + queryRequestOptions: new QueryRequestOptions() + { + MaxBufferedItemCount = 100, + MaxConcurrency = 100, + MaxItemCount = maxItemCount, + }); + + Assert.AreEqual(expected: 1, actual: actualAggregationQuery.Count()); + Assert.AreEqual( + expected: expectedAggregation, + actual: actualAggregationQuery.First(), + message: $"Results did not match for query: {query} with maxItemCount: {maxItemCount}" + + $"Actual: {actualAggregationQuery.First()}" + + $"Expected: {expectedAggregation}"); + } + } + + // ------------------------------------------ + // Negative + // ------------------------------------------ + + List notSupportedQueries = new List() + { + "SELECT MIN(c.age) + MAX(c.age) FROM c", + "SELECT MIN(c.age) / 2 FROM c", + }; + + foreach (string query in notSupportedQueries) + { + try + { + List actual = await QueryWithoutContinuationTokensAsync( + container: container, + query: query, + queryRequestOptions: new QueryRequestOptions() + { + MaxBufferedItemCount = 100, + MaxConcurrency = 100, + }); + + Assert.Fail("Expected Query To Fail"); + } + catch (Exception) + { + // Do Nothing + } + } + } + + [TestMethod] + public async Task TestArrayAggregatesWithContinuationTokenAsync() + { + await this.TestArrayAggregatesWithContinuationToken(100); + + // using 2048 + 1 documents here to ensure list size hits continuation token limit of 16KB + // We aggregate c.age (integers) which has 8 bytes, 16KB / 8B = 2048 + await this.TestArrayAggregatesWithContinuationToken(2049); + } + + private async Task TestArrayAggregatesWithContinuationToken(int numDocuments) + { + int seed = 135749376; + + Random rand = new Random(seed); + List people = new List(); + + for (int i = 0; i < numDocuments; i++) + { + // Generate random people + Person person = PersonGenerator.GetRandomPerson(rand); + for (int j = 0; j < rand.Next(0, 4); j++) + { + // Force an exact duplicate + people.Add(person); + } + } + + List documents = new List(); + // Shuffle them so they end up in different pages + people = people.OrderBy((person) => Guid.NewGuid()).ToList(); + foreach (Person person in people) + { + documents.Add(JsonConvert.SerializeObject(person)); + } + + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct | ConnectionModes.Gateway, + CollectionTypes.MultiPartition, + documents, + ImplementationAsync, + "/id"); + + async static Task ImplementationAsync(Container container, IReadOnlyList documents) + { + foreach (string[] queriesToCompare in new string[][] + { + new string[]{ "SELECT VALUE c.age FROM c", "SELECT VALUE MakeList(c.age) FROM c" }, + new string[]{ "SELECT DISTINCT VALUE c.age FROM c ORDER BY c.age", "SELECT VALUE MakeSet(c.age) FROM c" }, + }) + { + string queryWithoutAggregate = queriesToCompare[0]; + List expectedDocuments = await QueryTestsBase.RunQueryCombinationsAsync( + container, + queryWithoutAggregate, + new QueryRequestOptions() + { + MaxConcurrency = 10, + MaxItemCount = 100, + }, + QueryDrainingMode.ContinuationToken | QueryDrainingMode.HoldState); + + CosmosElement[] normalizedExpectedResult = expectedDocuments.ToArray(); + Array.Sort(normalizedExpectedResult); + + CosmosArray normalizedExpectedCosmosArray = CosmosArray.Create(normalizedExpectedResult); + + int[] pageSizes = (documents.Count() < 1000) ? new int[] { 1, 10, 100 } : new int[] { 100 }; + foreach (int pageSize in pageSizes) + { + string queryWithAggregate = queriesToCompare[1]; + List actualDocuments = await QueryTestsBase.RunQueryCombinationsAsync( + container, + queryWithAggregate, + new QueryRequestOptions() + { + MaxConcurrency = 10, + MaxItemCount = pageSize + }, + QueryDrainingMode.ContinuationToken | QueryDrainingMode.HoldState); + + CosmosElement aggregateResult = actualDocuments.First(); + CosmosArray normalizedActualCosmosArray = null; + if (aggregateResult is CosmosArray actualCosmosArray) + { + CosmosElement[] normalizedActualArray = actualCosmosArray.ToArray(); + Array.Sort(normalizedActualArray); + normalizedActualCosmosArray = CosmosArray.Create(normalizedActualArray); + } + + Assert.AreEqual( + expected: normalizedExpectedCosmosArray, + actual: normalizedActualCosmosArray, + message: $"Documents didn't match for {queryWithAggregate} on a Partitioned container"); + } + } } - } - - private static AggregateQueryArguments[] CreateAggregateQueryArguments( - IReadOnlyList inputDocuments, - AggregateTestArgs aggregateTestArgs) - { - IReadOnlyList documentsWherePkIsANumber = inputDocuments - .Where(doc => - { - return double.TryParse( - doc[aggregateTestArgs.PartitionKey].ToString(), - out double result); - }) - .ToList(); - double numberSum = documentsWherePkIsANumber - .Sum(doc => - { - if (!doc.TryGetValue(aggregateTestArgs.PartitionKey, out CosmosNumber number)) - { - Assert.Fail("Failed to get partition key from document"); - } - - return Number64.ToDouble(number.Value); - }); - double count = documentsWherePkIsANumber.Count(); - - IReadOnlyList makeListResult = inputDocuments - .Select(doc => - { - if (!doc.TryGetValue(aggregateTestArgs.PartitionKey, out CosmosElement cosmosElement)) - { - Assert.Fail("Failed to get partition key from document"); - } - - return cosmosElement; - }) - .ToList(); - - IReadOnlyList makeSetResult = makeListResult.Distinct().ToList(); - - AggregateQueryArguments[] aggregateQueryArgumentsList = new AggregateQueryArguments[] - { - new AggregateQueryArguments( - aggregateOperator: "AVG", - expectedValue: CosmosNumber64.Create(numberSum / count), - predicate: $"IS_NUMBER(r.{aggregateTestArgs.PartitionKey})"), - new AggregateQueryArguments( - aggregateOperator: "AVG", - expectedValue: CosmosUndefined.Create(), - predicate: "true"), - new AggregateQueryArguments( - aggregateOperator: "COUNT", - expectedValue: CosmosNumber64.Create(inputDocuments.Count()), - predicate: "true"), - new AggregateQueryArguments( - aggregateOperator: "MAKELIST", - expectedValue: CosmosArray.Create(makeListResult), - predicate: "true", - ignoreResultOrder: true), - new AggregateQueryArguments( - aggregateOperator: "MAKESET", - expectedValue: CosmosArray.Create(makeSetResult), - predicate: "true", - ignoreResultOrder: true), - new AggregateQueryArguments( - aggregateOperator: "MAX", - expectedValue: CosmosString.Create("xyz"), - predicate: "true"), - new AggregateQueryArguments( - aggregateOperator: "MIN", - expectedValue: CosmosBoolean.Create(false), - predicate: "true"), - new AggregateQueryArguments( - aggregateOperator: "SUM", - expectedValue: CosmosNumber64.Create(numberSum), - predicate: $"IS_NUMBER(r.{aggregateTestArgs.PartitionKey})"), - new AggregateQueryArguments( - aggregateOperator: "SUM", - expectedValue: CosmosUndefined.Create(), - predicate: $"true"), - }; - - return aggregateQueryArgumentsList; - } - - private readonly struct AggregateTestArgs - { - public AggregateTestArgs( - int numberOfDocumentsDifferentPartitionKey, - int numberOfDocsWithSamePartitionKey, - string partitionKey, - string uniquePartitionKey, - string field, - IReadOnlyList values) - { - this.NumberOfDocumentsDifferentPartitionKey = numberOfDocumentsDifferentPartitionKey; - this.NumberOfDocsWithSamePartitionKey = numberOfDocsWithSamePartitionKey; - this.PartitionKey = partitionKey; - this.UniquePartitionKey = uniquePartitionKey; - this.Field = field; - this.Values = values; - } - - public int NumberOfDocumentsDifferentPartitionKey { get; } - public int NumberOfDocsWithSamePartitionKey { get; } - public string PartitionKey { get; } - public string UniquePartitionKey { get; } - public string Field { get; } - public IReadOnlyList Values { get; } - } - - private readonly struct AggregateQueryArguments - { - public AggregateQueryArguments(string aggregateOperator, CosmosElement expectedValue, string predicate, bool ignoreResultOrder=false) - { - this.AggregateOperator = aggregateOperator; - this.ExpectedValue = expectedValue; - this.Predicate = predicate; - this.IgnoreResultOrder = ignoreResultOrder; - } - - public string AggregateOperator { get; } - public CosmosElement ExpectedValue { get; } - public string Predicate { get; } - public bool IgnoreResultOrder { get; } - - public override string ToString() - { - IJsonWriter writer = Cosmos.Json.JsonWriter.Create(JsonSerializationFormat.Text); - writer.WriteObjectStart(); - - writer.WriteFieldName(nameof(this.AggregateOperator)); - writer.WriteStringValue(this.AggregateOperator); - - writer.WriteFieldName(nameof(this.ExpectedValue)); - if (this.ExpectedValue is not CosmosUndefined) - { - writer.WriteStringValue(this.ExpectedValue.ToString()); - } - else - { - writer.WriteObjectStart(); - writer.WriteObjectEnd(); - } - - writer.WriteFieldName(nameof(this.Predicate)); - writer.WriteStringValue(this.Predicate); - - writer.WriteObjectEnd(); - - return Utf8StringHelpers.ToString(writer.GetResult()); - } - } - - [TestMethod] - public async Task TestAggregateFunctionsWithEmptyPartitionsAsync() - { - AggregateQueryEmptyPartitionsArgs testArgs = new AggregateQueryEmptyPartitionsArgs() - { - NumDocuments = 100, - PartitionKey = "key", - UniqueField = "UniqueField", - }; - - List inputDocuments = new List(testArgs.NumDocuments); - for (int i = 0; i < testArgs.NumDocuments; ++i) - { - Document doc = new Document(); - doc.SetPropertyValue(testArgs.PartitionKey, Guid.NewGuid()); - doc.SetPropertyValue(testArgs.UniqueField, i); - inputDocuments.Add(doc.ToString()); - } - - await this.CreateIngestQueryDeleteAsync( - ConnectionModes.Direct | ConnectionModes.Gateway, - CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, - inputDocuments, - ImplementationAsync, - testArgs, - "/" + testArgs.PartitionKey); - - async Task ImplementationAsync( - Container container, - IReadOnlyList documents, - AggregateQueryEmptyPartitionsArgs args) - { - int numDocuments = args.NumDocuments; - string partitionKey = args.PartitionKey; - string uniqueField = args.UniqueField; - - // Perform full fanouts but only match a single value that isn't the partition key. - // This leads to all other partitions returning { "" = UNDEFINDED, "count" = 0 } - // which should be ignored from the aggregation. - int valueOfInterest = args.NumDocuments / 2; - string[] queries = new string[] - { - $"SELECT VALUE AVG(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", - $"SELECT VALUE MIN(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", - $"SELECT VALUE MAX(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", - $"SELECT VALUE SUM(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", - }; - - foreach (string query in queries) - { - try - { - List items = await QueryTestsBase.RunQueryAsync( - container, - query, - new QueryRequestOptions() - { - MaxConcurrency = 10, - }); - - Assert.AreEqual(valueOfInterest, items.Single().ToDouble()); - } - catch (Exception ex) - { - Assert.Fail($"Something went wrong with query: {query}, ex: {ex}"); - } - } - - string[] arrayAggregateQueries = new string[] - { - $"SELECT VALUE MAKELIST(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", - $"SELECT VALUE MAKESET(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", - }; - - foreach (string query in arrayAggregateQueries) - { - try - { - List items = await QueryTestsBase.RunQueryAsync( - container, - query, - new QueryRequestOptions() - { - MaxConcurrency = 10, - }); - - Assert.IsTrue((items.Count() == 1) && (items.Single() is CosmosArray result) && result.Equals(CosmosArray.Create(CosmosNumber64.Create(valueOfInterest)))); - } - catch (Exception ex) - { - Assert.Fail($"Something went wrong with query: {query}, ex: {ex}"); - } - } - } - } - - private struct AggregateQueryEmptyPartitionsArgs - { - public int NumDocuments; - public string PartitionKey; - public string UniqueField; - } - - [TestMethod] - [Owner("brchon")] - public async Task TestNonValueAggregatesAsync() - { - string[] documents = new string[] - { - @"{""first"":""Good"",""last"":""Trevino"",""age"":23,""height"":61,""income"":59848}", - @"{""first"":""Charles"",""last"":""Decker"",""age"":31,""height"":64,""income"":55970}", - @"{""first"":""Holden"",""last"":""Cotton"",""age"":30,""height"":66,""income"":57075}", - @"{""first"":""Carlene"",""last"":""Cabrera"",""age"":26,""height"":72,""income"":98018}", - @"{""first"":""Gates"",""last"":""Spence"",""age"":38,""height"":53,""income"":12338}", - @"{""first"":""Camacho"",""last"":""Singleton"",""age"":40,""height"":52,""income"":76973}", - @"{""first"":""Rachel"",""last"":""Tucker"",""age"":27,""height"":68,""income"":28116}", - @"{""first"":""Kristi"",""last"":""Robertson"",""age"":32,""height"":53,""income"":61687}", - @"{""first"":""Poole"",""last"":""Petty"",""age"":22,""height"":75,""income"":53381}", - @"{""first"":""Lacey"",""last"":""Carlson"",""age"":38,""height"":78,""income"":63989}", - @"{""first"":""Rosario"",""last"":""Mendez"",""age"":21,""height"":64,""income"":20300}", - @"{""first"":""Estrada"",""last"":""Collins"",""age"":28,""height"":74,""income"":6926}", - @"{""first"":""Ursula"",""last"":""Burton"",""age"":26,""height"":66,""income"":32870}", - @"{""first"":""Rochelle"",""last"":""Sanders"",""age"":24,""height"":56,""income"":47564}", - @"{""first"":""Darcy"",""last"":""Herring"",""age"":27,""height"":52,""income"":67436}", - @"{""first"":""Carole"",""last"":""Booth"",""age"":34,""height"":60,""income"":50177}", - @"{""first"":""Cruz"",""last"":""Russell"",""age"":25,""height"":52,""income"":95072}", - @"{""first"":""Wilma"",""last"":""Robbins"",""age"":36,""height"":50,""income"":53008}", - @"{""first"":""Mcdaniel"",""last"":""Barlow"",""age"":21,""height"":78,""income"":85441}", - @"{""first"":""Leann"",""last"":""Blackwell"",""age"":40,""height"":79,""income"":900}", - @"{""first"":""Hoffman"",""last"":""Hoffman"",""age"":31,""height"":76,""income"":1208}", - @"{""first"":""Pittman"",""last"":""Shepherd"",""age"":35,""height"":61,""income"":26887}", - @"{""first"":""Wright"",""last"":""Rojas"",""age"":35,""height"":73,""income"":76487}", - @"{""first"":""Lynne"",""last"":""Waters"",""age"":27,""height"":60,""income"":22926}", - @"{""first"":""Corina"",""last"":""Shelton"",""age"":29,""height"":78,""income"":67379}", - @"{""first"":""Alvarez"",""last"":""Barr"",""age"":29,""height"":59,""income"":34698}", - @"{""first"":""Melinda"",""last"":""Mccoy"",""age"":24,""height"":63,""income"":69811}", - @"{""first"":""Chelsea"",""last"":""Bolton"",""age"":20,""height"":63,""income"":47698}", - @"{""first"":""English"",""last"":""Ingram"",""age"":28,""height"":50,""income"":94977}", - @"{""first"":""Vance"",""last"":""Thomas"",""age"":30,""height"":49,""income"":67638}", - @"{""first"":""Howell"",""last"":""Joyner"",""age"":34,""height"":78,""income"":65547}", - @"{""first"":""Ofelia"",""last"":""Chapman"",""age"":23,""height"":82,""income"":85049}", - @"{""first"":""Downs"",""last"":""Adams"",""age"":28,""height"":76,""income"":19373}", - @"{""first"":""Terrie"",""last"":""Bryant"",""age"":32,""height"":55,""income"":79024}", - @"{""first"":""Jeanie"",""last"":""Carson"",""age"":26,""height"":52,""income"":68293}", - @"{""first"":""Hazel"",""last"":""Bean"",""age"":40,""height"":70,""income"":46028}", - @"{""first"":""Dominique"",""last"":""Norman"",""age"":25,""height"":50,""income"":59445}", - @"{""first"":""Lyons"",""last"":""Patterson"",""age"":36,""height"":64,""income"":71748}", - @"{""first"":""Catalina"",""last"":""Cantrell"",""age"":30,""height"":78,""income"":16999}", - @"{""first"":""Craft"",""last"":""Head"",""age"":30,""height"":49,""income"":10542}", - @"{""first"":""Suzanne"",""last"":""Gilliam"",""age"":36,""height"":77,""income"":7511}", - @"{""first"":""Pamela"",""last"":""Merritt"",""age"":30,""height"":81,""income"":80653}", - @"{""first"":""Haynes"",""last"":""Ayala"",""age"":38,""height"":65,""income"":85832}", - @"{""first"":""Teri"",""last"":""Martin"",""age"":40,""height"":83,""income"":27839}", - @"{""first"":""Susanne"",""last"":""Short"",""age"":25,""height"":57,""income"":48957}", - @"{""first"":""Rosalie"",""last"":""Camacho"",""age"":24,""height"":83,""income"":30313}", - @"{""first"":""Walls"",""last"":""Bray"",""age"":28,""height"":74,""income"":21616}", - @"{""first"":""Norris"",""last"":""Bates"",""age"":23,""height"":59,""income"":13631}", - @"{""first"":""Wendy"",""last"":""King"",""age"":38,""height"":48,""income"":19845}", - @"{""first"":""Deena"",""last"":""Ramsey"",""age"":20,""height"":66,""income"":49665}", - @"{""first"":""Richmond"",""last"":""Meadows"",""age"":36,""height"":59,""income"":43244}", - @"{""first"":""Burks"",""last"":""Whitley"",""age"":25,""height"":55,""income"":39974}", - @"{""first"":""Gilliam"",""last"":""George"",""age"":37,""height"":82,""income"":47114}", - @"{""first"":""Marcy"",""last"":""Harding"",""age"":33,""height"":80,""income"":20316}", - @"{""first"":""Curtis"",""last"":""Gomez"",""age"":31,""height"":50,""income"":69085}", - @"{""first"":""Lopez"",""last"":""Burt"",""age"":34,""height"":79,""income"":37577}", - @"{""first"":""Nell"",""last"":""Nixon"",""age"":37,""height"":58,""income"":67999}", - @"{""first"":""Sonja"",""last"":""Lamb"",""age"":37,""height"":53,""income"":92553}", - @"{""first"":""Owens"",""last"":""Fischer"",""age"":40,""height"":48,""income"":75199}", - @"{""first"":""Ortega"",""last"":""Padilla"",""age"":28,""height"":55,""income"":29126}", - @"{""first"":""Stacie"",""last"":""Velez"",""age"":20,""height"":56,""income"":45292}", - @"{""first"":""Brennan"",""last"":""Craig"",""age"":38,""height"":65,""income"":37445}" - }; - - await this.CreateIngestQueryDeleteAsync( - ConnectionModes.Direct | ConnectionModes.Gateway, - /*CollectionTypes.SinglePartition |*/ CollectionTypes.MultiPartition, - documents, - this.TestNonValueAggregates); - } - - private async Task TestNonValueAggregates( - Container container, - IReadOnlyList documents) - { - // ------------------------------------------ - // Positive - // ------------------------------------------ - - List<(string, CosmosElement)> queryAndExpectedAggregation = new List<(string, CosmosElement)>() - { - // ------------------------------------------ - // Simple Aggregates without a value - // ------------------------------------------ - - ( - "SELECT SUM(c.age) FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "$1", - CosmosNumber64.Create( - documents.Sum(document => document["age"].ToDouble())) - } - }) - ), - - ( - "SELECT COUNT(c.age) FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "$1", - CosmosNumber64.Create( - documents.Where(document => document.TryGetValue("age", out _)).Count()) - } - }) - ), - - ( - "SELECT MIN(c.age) FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "$1", - CosmosNumber64.Create( - documents.Min(document => document["age"].ToDouble())) - } - }) - ), - - ( - "SELECT MAX(c.age) FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "$1", - CosmosNumber64.Create( - documents.Max(document => document["age"].ToDouble())) - } - }) - ), - - ( - "SELECT AVG(c.age) FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "$1", - CosmosNumber64.Create( - documents.Average(document => document["age"].ToDouble())) - } - }) - ), - - // ------------------------------------------ - // Simple aggregates with alias - // ------------------------------------------ - - ( - "SELECT SUM(c.age) as sum_age FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "sum_age", - CosmosNumber64.Create( - documents.Sum(document => document["age"].ToDouble())) - } - }) - ), - - ( - "SELECT COUNT(c.age) as count_age FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "count_age", - CosmosNumber64.Create( - documents.Where(document => document.TryGetValue("age", out _)).Count()) - } - }) - ), - - ( - "SELECT MIN(c.age) as min_age FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "min_age", - CosmosNumber64.Create( - documents.Min(document => document["age"].ToDouble())) - } - }) - ), - - ( - "SELECT MAX(c.age) as max_age FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "max_age", - CosmosNumber64.Create( - documents.Max(document => document["age"].ToDouble())) - } - }) - ), - - ( - "SELECT AVG(c.age) as avg_age FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "avg_age", - CosmosNumber64.Create( - documents.Average(document => document["age"].ToDouble())) - } - }) - ), - - // ------------------------------------------ - // Multiple Aggregates without alias - // ------------------------------------------ - - ( - "SELECT MIN(c.age), MAX(c.age) FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "$1", - CosmosNumber64.Create( - documents.Min(document => document["age"].ToDouble())) - }, - { - "$2", - CosmosNumber64.Create( - documents.Max(document => document["age"].ToDouble())) - }, - }) - ), - - // ------------------------------------------ - // Multiple Aggregates with alias - // ------------------------------------------ - - ( - "SELECT MIN(c.age) as min_age, MAX(c.age) as max_age FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "min_age", - CosmosNumber64.Create( - documents.Min(document => document["age"].ToDouble())) - }, - { - "max_age", - CosmosNumber64.Create( - documents.Max(document => document["age"].ToDouble())) - }, - }) - ), - - // ------------------------------------------ - // Multiple Aggregates with and without alias - // ------------------------------------------ - - ( - "SELECT MIN(c.age), MAX(c.age) as max_age FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "$1", - CosmosNumber64.Create( - documents.Min(document => document["age"].ToDouble())) - }, - { - "max_age", - CosmosNumber64.Create( - documents.Max(document => document["age"].ToDouble())) - }, - }) - ), - - ( - "SELECT MIN(c.age) as min_age, MAX(c.age) FROM c", - CosmosObject.Create( - new Dictionary() - { - { - "min_age", - CosmosNumber64.Create( - documents.Min(document => document["age"].ToDouble())) - }, - { - "$1", - CosmosNumber64.Create( - documents.Max(document => document["age"].ToDouble())) - }, - }) - ), - }; - - // Test query correctness. - foreach ((string query, CosmosElement expectedAggregation) in queryAndExpectedAggregation) - { - foreach (int maxItemCount in new int[] { 1, 5, 10 }) - { - List actualAggregationQuery = await RunQueryAsync( - container: container, - query: query, - queryRequestOptions: new QueryRequestOptions() - { - MaxBufferedItemCount = 100, - MaxConcurrency = 100, - MaxItemCount = maxItemCount, - }); - - Assert.AreEqual(expected: 1, actual: actualAggregationQuery.Count()); - Assert.AreEqual( - expected: expectedAggregation, - actual: actualAggregationQuery.First(), - message: $"Results did not match for query: {query} with maxItemCount: {maxItemCount}" + - $"Actual: {actualAggregationQuery.First()}" + - $"Expected: {expectedAggregation}"); - } - } - - // ------------------------------------------ - // Negative - // ------------------------------------------ - - List notSupportedQueries = new List() - { - "SELECT MIN(c.age) + MAX(c.age) FROM c", - "SELECT MIN(c.age) / 2 FROM c", - }; - - foreach (string query in notSupportedQueries) - { - try - { - List actual = await QueryWithoutContinuationTokensAsync( - container: container, - query: query, - queryRequestOptions: new QueryRequestOptions() - { - MaxBufferedItemCount = 100, - MaxConcurrency = 100, - }); - - Assert.Fail("Expected Query To Fail"); - } - catch (Exception) - { - // Do Nothing - } - } - } - - [TestMethod] - public async Task TestArrayAggregatesWithContinuationTokenAsync() - { - await this.TestArrayAggregatesWithContinuationToken(100); - - // using 2048 + 1 documents here to ensure list size hits continuation token limit of 16KB - // We aggregate c.age (integers) which has 8 bytes, 16KB / 8B = 2048 - await this.TestArrayAggregatesWithContinuationToken(2049); - } - - private async Task TestArrayAggregatesWithContinuationToken(int numDocuments) - { - int seed = 135749376; - - Random rand = new Random(seed); - List people = new List(); - - for (int i = 0; i < numDocuments; i++) - { - // Generate random people - Person person = PersonGenerator.GetRandomPerson(rand); - for (int j = 0; j < rand.Next(0, 4); j++) - { - // Force an exact duplicate - people.Add(person); - } - } - - List documents = new List(); - // Shuffle them so they end up in different pages - people = people.OrderBy((person) => Guid.NewGuid()).ToList(); - foreach (Person person in people) - { - documents.Add(JsonConvert.SerializeObject(person)); - } - - await this.CreateIngestQueryDeleteAsync( - ConnectionModes.Direct | ConnectionModes.Gateway, - CollectionTypes.MultiPartition, - documents, - ImplementationAsync, - "/id"); - - async static Task ImplementationAsync(Container container, IReadOnlyList documents) - { - foreach (string[] queriesToCompare in new string[][] - { - new string[]{ "SELECT VALUE c.age FROM c", "SELECT VALUE MakeList(c.age) FROM c" }, - new string[]{ "SELECT DISTINCT VALUE c.age FROM c ORDER BY c.age", "SELECT VALUE MakeSet(c.age) FROM c" }, - }) - { - string queryWithoutAggregate = queriesToCompare[0]; - List expectedDocuments = await QueryTestsBase.RunQueryCombinationsAsync( - container, - queryWithoutAggregate, - new QueryRequestOptions() - { - MaxConcurrency = 10, - MaxItemCount = 100, - }, - QueryDrainingMode.ContinuationToken | QueryDrainingMode.HoldState); - - CosmosElement[] normalizedExpectedResult = expectedDocuments.ToArray(); - Array.Sort(normalizedExpectedResult); - - CosmosArray normalizedExpectedCosmosArray = CosmosArray.Create(normalizedExpectedResult); - - int[] pageSizes = (documents.Count() < 1000) ? new int[] { 1, 10, 100 } : new int[] { 100 }; - foreach (int pageSize in pageSizes) - { - string queryWithAggregate = queriesToCompare[1]; - List actualDocuments = await QueryTestsBase.RunQueryCombinationsAsync( - container, - queryWithAggregate, - new QueryRequestOptions() - { - MaxConcurrency = 10, - MaxItemCount = pageSize - }, - QueryDrainingMode.ContinuationToken | QueryDrainingMode.HoldState); - - CosmosElement aggregateResult = actualDocuments.First(); - CosmosArray normalizedActualCosmosArray = null; - if (aggregateResult is CosmosArray actualCosmosArray) - { - CosmosElement[] normalizedActualArray = actualCosmosArray.ToArray(); - Array.Sort(normalizedActualArray); - normalizedActualCosmosArray = CosmosArray.Create(normalizedActualArray); - } - - Assert.AreEqual( - expected: normalizedExpectedCosmosArray, - actual: normalizedActualCosmosArray, - message: $"Documents didn't match for {queryWithAggregate} on a Partitioned container"); - } - } - } - } - } -} + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/BypassQueryParsingTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/BypassQueryParsingTests.cs index 4db276f4ce..5485b9d16e 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/BypassQueryParsingTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/BypassQueryParsingTests.cs @@ -2,44 +2,116 @@ namespace Microsoft.Azure.Cosmos.EmulatorTests.Query { using System; using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.CosmosElements; + using Microsoft.Azure.Cosmos.Query.Core; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] [TestCategory("Query")] public sealed class BypassQueryParsingTests : QueryTestsBase { - [TestMethod] - public async Task TestBypassQueryParsingWithNonePartitionKey() + private const int DocumentCount = 400; + + private static readonly Documents.PartitionKeyDefinition HierarchicalPartitionKeyDefinition = new Documents.PartitionKeyDefinition { - int documentCount = 400; + Paths = new Collection { "/nullField", "/numberField" }, + Kind = Documents.PartitionKind.MultiHash, + Version = Documents.PartitionKeyDefinitionVersion.V2 + }; - string query = "SELECT VALUE r.numberField FROM r"; - IReadOnlyList expectedOutput = Enumerable.Range(0, documentCount).Select(i => i.ToString()).ToList(); + private static readonly Documents.PartitionKeyDefinition SimplePartitionKeyDefinition = new Documents.PartitionKeyDefinition + { + Paths = new Collection { "/UndefinedField" }, + Kind = Documents.PartitionKind.Hash + }; - await this.ValidateQueryBypassWithNonePartitionKey(documentCount, query, expectedOutput); + [TestMethod] + [TestCategory("Query")] + public async Task TestBypassQueryParsing() + { + IReadOnlyList testCases = new List + { + new( + PartitionKey.None, + "SELECT VALUE r.numberField FROM r", + Enumerable.Range(0, DocumentCount).Select(i => i.ToString()).ToList(), + TestInjections.PipelineType.Passthrough), + new( + PartitionKey.None, + @"SELECT VALUE { """" : r.numberField } FROM r", + Enumerable.Range(0, DocumentCount).Select(i => String.Format("{{\"\":{0}}}", i)).ToList(), + TestInjections.PipelineType.Passthrough), + }; + + await this.ValidateQueryBypass( + ConnectionModes.Direct | ConnectionModes.Gateway, + CollectionTypes.NonPartitioned | CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, + SimplePartitionKeyDefinition, + testCases); } [TestMethod] [TestCategory("Query")] - public async Task TestBypassQueryParsingWithNonePartitionKeyEmptyPropertyName() + public async Task TestBypassQueryParsingWithHPK() { - int documentCount = 400; - - string query = @"SELECT VALUE { """" : r.numberField } FROM r"; - IReadOnlyList expectedOutput = Enumerable.Range(0, documentCount).Select(i => String.Format("{{\"\":{0}}}", i)).ToList(); + PartitionKey partialPartitionKey = new PartitionKeyBuilder().AddNullValue().Build(); - await this.ValidateQueryBypassWithNonePartitionKey(documentCount, query, expectedOutput); + IReadOnlyList testCases = new List + { + new( + partialPartitionKey, + "SELECT VALUE r.numberField FROM r", + Enumerable.Range(0, DocumentCount).Select(i => i.ToString()).ToList(), + TestInjections.PipelineType.Passthrough), // Passthrough because it is a client streaming query + new( + partialPartitionKey, + $"SELECT TOP {DocumentCount} VALUE r.numberField FROM r", + Enumerable.Range(0, DocumentCount).Select(i => i.ToString()).ToList(), + TestInjections.PipelineType.Specialized), + new( + partialPartitionKey, + @"SELECT VALUE { """" : r.numberField } FROM r", + Enumerable.Range(0, DocumentCount).Select(i => String.Format("{{\"\":{0}}}", i)).ToList(), + TestInjections.PipelineType.Passthrough), // Passthrough because it is a client streaming query + new( + partialPartitionKey, + "SELECT VALUE r.numberField FROM r ORDER BY r.numberField", + Enumerable.Range(0, DocumentCount).Select(i => i.ToString()).ToList(), + TestInjections.PipelineType.Specialized), + }; + + await this.ValidateQueryBypass( + ConnectionModes.Gateway, + CollectionTypes.MultiPartition, + HierarchicalPartitionKeyDefinition, + testCases); } - private async Task ValidateQueryBypassWithNonePartitionKey(int documentCount, string query, IReadOnlyList expectedOutput) + private Task ValidateQueryBypass(ConnectionModes connectionModes, CollectionTypes collectionTypes, Documents.PartitionKeyDefinition partitionKeyDefinition, IReadOnlyList testCases) { - QueryRequestOptions feedOptions = new QueryRequestOptions { PartitionKey = PartitionKey.None }; + IReadOnlyList documents = CreateDocuments(DocumentCount); - async Task ImplementationAsync(Container container, IReadOnlyList documents) + return this.CreateIngestQueryDeleteAsync( + connectionModes, + collectionTypes, + documents, + query: (container, _) => RunTestsAsync(container, testCases), + partitionKeyDefinition); + } + + private static async Task RunTestsAsync(Container container, IReadOnlyList testCases) + { + foreach (QueryTestCase testCase in testCases) { + QueryRequestOptions feedOptions = new QueryRequestOptions + { + PartitionKey = testCase.PartitionKey, + TestSettings = new TestInjections(simulate429s: false, simulateEmptyPages: false, responseStats: new()) + }; + ContainerInternal containerCore = container as ContainerInlineCore; MockCosmosQueryClient cosmosQueryClientCore = new MockCosmosQueryClient( @@ -53,20 +125,19 @@ async Task ImplementationAsync(Container container, IReadOnlyList containerCore.Id, cosmosQueryClientCore); - List items = await RunQueryAsync(containerWithBypassParsing, query, feedOptions); + List items = await RunQueryAsync(containerWithBypassParsing, testCase.Query, feedOptions); string[] actualOutput = items.Select(x => x.ToString()).ToArray(); - Assert.IsTrue(expectedOutput.SequenceEqual(actualOutput)); - } + if(!testCase.ExpectedOutput.SequenceEqual(actualOutput)) + { + System.Diagnostics.Trace.WriteLine($"Expected: [{string.Join(", ", testCase.ExpectedOutput)}]"); + System.Diagnostics.Trace.WriteLine($"Actual: [{string.Join(", ", actualOutput)}]"); - IReadOnlyList documents = CreateDocuments(documentCount); + Assert.Fail("Query results do not match expected results."); + } - await this.CreateIngestQueryDeleteAsync( - ConnectionModes.Direct | ConnectionModes.Gateway, - CollectionTypes.NonPartitioned | CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, - documents, - ImplementationAsync, - "/undefinedPartitionKey"); + Assert.AreEqual(testCase.ExpectedPipelineType, feedOptions.TestSettings.Stats.PipelineType); + } } private static IReadOnlyList CreateDocuments(int documentCount) @@ -80,5 +151,24 @@ private static IReadOnlyList CreateDocuments(int documentCount) return documents; } + + private sealed class QueryTestCase + { + public PartitionKey PartitionKey { get; } + + public string Query { get; } + + public IReadOnlyList ExpectedOutput { get; } + + public TestInjections.PipelineType ExpectedPipelineType { get; } + + public QueryTestCase(PartitionKey partitionKey, string query, IReadOnlyList expectedOutput, TestInjections.PipelineType expectedPipelineType) + { + this.PartitionKey = partitionKey; + this.Query = query ?? throw new ArgumentNullException(nameof(query)); + this.ExpectedOutput = expectedOutput ?? throw new ArgumentNullException(nameof(expectedOutput)); + this.ExpectedPipelineType = expectedPipelineType; + } + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/CosmosUndefinedQueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/CosmosUndefinedQueryTests.cs index 436e9dc771..eb57080188 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/CosmosUndefinedQueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/CosmosUndefinedQueryTests.cs @@ -76,42 +76,42 @@ private static async Task HybridSearchTests(Container container, IReadOnlyList>{ new List{ 2, 57, 85 }, new List{ 2, 85, 57 } }), + MakeSanityTest(@" + SELECT c.index AS Index, c.title AS Title, c.text AS Text + FROM c + WHERE (FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John')) AND (c.index = 2) + ORDER BY RANK FullTextScore(c.title, 'John')", + new List>{ new List{ 2 } }), + MakeSanityTest(@" + SELECT c.index AS Index, c.title AS Title, c.text AS Text + FROM c + WHERE FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John') + ORDER BY RANK FullTextScore(c.title, 'John')", + new List>{ new List{ 2 } }, + new PartitionKey(2)), MakeSanityTest(@" SELECT TOP 10 c.index AS Index, c.title AS Title, c.text AS Text FROM c WHERE FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John') - ORDER BY RANK FullTextScore(c.title, ['John'])", + ORDER BY RANK FullTextScore(c.title, 'John')", new List>{ new List{ 2, 57, 85 }, new List{ 2, 85, 57 } }), MakeSanityTest(@" SELECT c.index AS Index, c.title AS Title, c.text AS Text FROM c WHERE FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John') - ORDER BY RANK FullTextScore(c.title, ['John']) + ORDER BY RANK FullTextScore(c.title, 'John') OFFSET 1 LIMIT 5", new List>{ new List{ 57, 85 }, new List{ 85, 57 } }), MakeSanityTest(@" SELECT c.index AS Index, c.title AS Title, c.text AS Text FROM c WHERE FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John') OR FullTextContains(c.text, 'United States') - ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States']))", + ORDER BY RANK RRF(FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States'))", new List>{ new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 2, 22, 57, 85 }, new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 2, 22, 85, 57 }, @@ -93,13 +106,13 @@ ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['Unit SELECT TOP 10 c.index AS Index, c.title AS Title, c.text AS Text FROM c WHERE FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John') OR FullTextContains(c.text, 'United States') - ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States']))", + ORDER BY RANK RRF(FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States'))", new List>{ new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 2 } }), MakeSanityTest(@" SELECT c.index AS Index, c.title AS Title, c.text AS Text FROM c WHERE FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John') OR FullTextContains(c.text, 'United States') - ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States'])) + ORDER BY RANK RRF(FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States')) OFFSET 5 LIMIT 10", new List>{ new List{ 24, 77, 76, 80, 2, 22, 57, 85 }, @@ -108,28 +121,28 @@ ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['Unit MakeSanityTest(@" SELECT TOP 10 c.index AS Index, c.title AS Title, c.text AS Text FROM c - ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States']))", + ORDER BY RANK RRF(FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States'))", new List>{new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 2 } }), MakeSanityTest(@" SELECT c.index AS Index, c.title AS Title, c.text AS Text FROM c - ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States'])) + ORDER BY RANK RRF(FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States')) OFFSET 0 LIMIT 11", new List>{ new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 2, 22 } }), MakeSanityTest($@" SELECT TOP 10 c.index AS Index, c.title AS Title, c.text AS Text FROM c - ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States']), VectorDistance(c.vector, {SampleVector}))", + ORDER BY RANK RRF(FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States'), VectorDistance(c.vector, {SampleVector}))", new List>{new List{ 21, 37, 75, 26, 35, 24, 87, 55, 49, 9 } }), MakeSanityTest($@" SELECT TOP 10 c.index AS Index, c.title AS Title, c.text AS Text FROM c - ORDER BY RANK RRF(VectorDistance(c.vector, {SampleVector}), FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States']))", + ORDER BY RANK RRF(VectorDistance(c.vector, {SampleVector}), FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States'))", new List>{new List{ 21, 37, 75, 26, 35, 24, 87, 55, 49, 9 } }), MakeSanityTest($@" SELECT TOP 10 c.index AS Index, c.title AS Title, c.text AS Text FROM c - ORDER BY RANK RRF(VectorDistance(c.vector, {SampleVector}), FullTextScore(c.title, ['John']), VectorDistance(c.image, {SampleVector}), VectorDistance(c.backup_image, {SampleVector}), FullTextScore(c.text, ['United States']))", + ORDER BY RANK RRF(VectorDistance(c.vector, {SampleVector}), FullTextScore(c.title, 'John'), VectorDistance(c.image, {SampleVector}), VectorDistance(c.backup_image, {SampleVector}), FullTextScore(c.text, 'United States'))", new List>{new List{ 21, 37, 75, 26, 35, 24, 87, 55, 49, 9 } }), }; @@ -137,7 +150,6 @@ ORDER BY RANK RRF(VectorDistance(c.vector, {SampleVector}), FullTextScore(c.titl } [TestMethod] - [Ignore("This test is disabled because it needs an emulator refresh.")] public async Task WeightedRankFusionTests() { List testCases = new List @@ -146,35 +158,32 @@ public async Task WeightedRankFusionTests() SELECT c.index AS Index, c.title AS Title, c.text AS Text FROM c WHERE FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John') OR FullTextContains(c.text, 'United States') - ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States']), [1, 1])", + ORDER BY RANK RRF(FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States'), [1, 1])", new List>{ - new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 25, 22, 2, 66, 57, 85 }, - new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 25, 22, 2, 66, 85, 57 }, + new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 2, 22, 85, 57 }, + new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 2, 22, 57, 85 }, }), MakeSanityTest(@" SELECT c.index AS Index, c.title AS Title, c.text AS Text FROM c WHERE FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John') OR FullTextContains(c.text, 'United States') - ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States']), [10, 10])", + ORDER BY RANK RRF(FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States'), [10, 10])", new List>{ - new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 25, 22, 2, 66, 57, 85 }, - new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 25, 22, 2, 66, 85, 57 }, + new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 2, 22, 57, 85 }, + new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 2, 22, 85, 57 }, }), MakeSanityTest(@" SELECT TOP 10 c.index AS Index, c.title AS Title, c.text AS Text FROM c WHERE FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John') OR FullTextContains(c.text, 'United States') - ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States']), [0.1, 0.1])", - new List>{ new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 25 } }), + ORDER BY RANK RRF(FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States'), [0.1, 0.1])", + new List>{ new List{ 61, 51, 49, 54, 75, 24, 77, 76, 80, 2 } }), MakeSanityTest(@" SELECT c.index AS Index, c.title AS Title, c.text AS Text FROM c WHERE FullTextContains(c.title, 'John') OR FullTextContains(c.text, 'John') OR FullTextContains(c.text, 'United States') - ORDER BY RANK RRF(FullTextScore(c.title, ['John']), FullTextScore(c.text, ['United States']), [-1, -1])", - new List>{ - new List{ 85, 57, 66, 2, 22, 25, 77, 76, 80, 75, 24, 49, 54, 51, 81 }, - new List{ 57, 85, 2, 66, 22, 25, 80, 76, 77, 24, 75, 54, 49, 51, 61 }, - }), + ORDER BY RANK RRF(FullTextScore(c.title, 'John'), FullTextScore(c.text, 'United States'), [-1, -1])", + new List>{ new List{ 57, 85, 2, 22, 80, 76, 77, 24, 75, 54, 49, 51, 61 } }), }; await this.RunTests(testCases); @@ -190,41 +199,55 @@ await this.CreateIngestQueryDeleteAsync( collectionTypes: CollectionTypes.MultiPartition, // | CollectionTypes.SinglePartition, documents: documents, query: (container, _) => RunTests(container, testCases), + partitionKey: "/index", indexingPolicy: CompositeIndexPolicy); } private static async Task RunTests(Container container, IEnumerable testCases) { - foreach (SanityTestCase testCase in testCases) + foreach (FullTextScoreScope fullTextScoreScope in new[]{ FullTextScoreScope.Local, FullTextScoreScope.Global }) { - List result = await RunQueryCombinationsAsync( - container, - testCase.Query, - queryRequestOptions: null, - queryDrainingMode: QueryDrainingMode.HoldState); + foreach (SanityTestCase testCase in testCases) + { + QueryRequestOptions testRequestOptions = new QueryRequestOptions + { + FullTextScoreScope = fullTextScoreScope, + }; - IEnumerable actual = result.Select(document => document.Index); + if (testCase.PartitionKey.HasValue) + { + testRequestOptions.PartitionKey = testCase.PartitionKey; + } - bool match = false; - foreach (IReadOnlyList expectedIndices in testCase.ExpectedIndices) - { - if (expectedIndices.SequenceEqual(actual)) + List result = await RunQueryCombinationsAsync( + container, + testCase.Query, + queryRequestOptions: testRequestOptions, + queryDrainingMode: QueryDrainingMode.HoldState); + + IEnumerable actual = result.Select(document => document.Index); + + bool match = false; + foreach (IReadOnlyList expectedIndices in testCase.ExpectedIndices) { - match = true; - break; + if (expectedIndices.SequenceEqual(actual)) + { + match = true; + break; + } } - } - if (!match) - { - Trace.WriteLine($"Query: {testCase.Query}"); - Trace.WriteLine($"Actual: {string.Join(", ", actual)}"); + if (!match) + { + Trace.WriteLine($"Query: {testCase.Query}"); + Trace.WriteLine($"Actual: {string.Join(", ", actual)}"); - string errorMessage = @"The query results did not match any of the expected results." + - "Please set HybridSearchCrossPartitionQueryPipelineStage.HybridSearchDebugTraceHelpers.Enabled = true to debug." + - "Usually, the failure may be due to some swaps in the results that have equal scores. You can see this in the debug output." + - "The solution is to add another expected result that matches the actual results (provided the scores are in decresing order)."; - Assert.Fail(errorMessage); + string errorMessage = @"The query results did not match any of the expected results." + + "Please set HybridSearchCrossPartitionQueryPipelineStage.HybridSearchDebugTraceHelpers.Enabled = true to debug." + + "Usually, the failure may be due to some swaps in the results that have equal scores. You can see this in the debug output." + + "The solution is to add another expected result that matches the actual results (provided the scores are in decresing order)."; + Assert.Fail(errorMessage); + } } } } @@ -254,12 +277,13 @@ private static IndexingPolicy CreateIndexingPolicy() return policy; } - private static SanityTestCase MakeSanityTest(string query, IReadOnlyList> expectedIndices) + private static SanityTestCase MakeSanityTest(string query, IReadOnlyList> expectedIndices, PartitionKey? partitionKey = null) { return new SanityTestCase { Query = query, ExpectedIndices = expectedIndices, + PartitionKey = partitionKey, }; } @@ -268,6 +292,8 @@ private sealed class SanityTestCase public string Query { get; init; } public IReadOnlyList> ExpectedIndices { get; init; } + + public PartitionKey? PartitionKey { get; init; } } private sealed class TextDocument diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/NonStreamingOrderByQueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/NonStreamingOrderByQueryTests.cs index 7881745f37..5862b00814 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/NonStreamingOrderByQueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/NonStreamingOrderByQueryTests.cs @@ -111,7 +111,7 @@ static async Task ImplementationAsync(Container container, IReadOnlyList SampleDocuments = new List + { + @"{""id"":""id0"",""operation"":""create"",""duration"":45,""tenant"":""tenant0"",""user"":""user0"",""session"":""session0""}", + @"{""id"":""id1"",""operation"":""update"",""duration"":23,""tenant"":""tenant0"",""user"":""user0"",""session"":""session1""}", + @"{""id"":""id2"",""operation"":""delete"",""duration"":67,""tenant"":""tenant0"",""user"":""user1"",""session"":""session0""}", + @"{""id"":""id3"",""operation"":""create"",""duration"":89,""tenant"":""tenant0"",""user"":""user1"",""session"":""session1""}", + @"{""id"":""id4"",""operation"":""update"",""duration"":12,""tenant"":""tenant1"",""user"":""user0"",""session"":""session0""}", + @"{""id"":""id5"",""operation"":""delete"",""duration"":56,""tenant"":""tenant1"",""user"":""user0"",""session"":""session1""}", + @"{""id"":""id6"",""operation"":""create"",""duration"":34,""tenant"":""tenant1"",""user"":""user1"",""session"":""session0""}", + @"{""id"":""id7"",""operation"":""update"",""duration"":78,""tenant"":""tenant1"",""user"":""user1"",""session"":""session1""}", + @"{""id"":""id8"",""operation"":""delete"",""duration"":91,""tenant"":""tenant2"",""user"":""user0"",""session"":""session0""}", + @"{""id"":""id9"",""operation"":""create"",""duration"":5,""tenant"":""tenant2"",""user"":""user1"",""session"":""session1""}", + @"{""id"":""id10"",""operation"":""update"",""duration"":42,""tenant"":""tenant0"",""user"":""user0"",""session"":""session2""}", + @"{""id"":""id11"",""operation"":""delete"",""duration"":73,""tenant"":""tenant1"",""user"":""user2"",""session"":""session0""}", + @"{""id"":""id12"",""operation"":""create"",""duration"":28,""tenant"":""tenant2"",""user"":""user0"",""session"":""session1""}", + @"{""id"":""id13"",""operation"":""update"",""duration"":61,""tenant"":""tenant0"",""user"":""user2"",""session"":""session2""}", + @"{""id"":""id14"",""operation"":""delete"",""duration"":15,""tenant"":""tenant1"",""user"":""user1"",""session"":""session2""}", + }; + + private static PartitionKeyDefinition HierarchicalPartitionKeyDefinition => + new PartitionKeyDefinition + { + Paths = new Collection { "/tenant", "/user", "/session" }, + Kind = PartitionKind.MultiHash, + Version = Documents.PartitionKeyDefinitionVersion.V2 + }; + + private static PartitionKeyDefinition SinglePartitionKeyDefinition => + new PartitionKeyDefinition + { + Paths = new Collection { "/tenant" }, + Kind = PartitionKind.Hash, + Version = Documents.PartitionKeyDefinitionVersion.V2 + }; + + [TestMethod] + public async Task TestSinglePartitionKeyContainer_Direct_SinglePartition() + { + await this.ExecuteTest( + SinglePartitionKeyDefinition, + ConnectionModes.Direct, + CollectionTypes.MultiPartition); + } + + [TestMethod] + public async Task TestSinglePartitionKeyContainer_Gateway_SinglePartition() + { + await this.ExecuteTest( + SinglePartitionKeyDefinition, + ConnectionModes.Gateway, + CollectionTypes.SinglePartition); + } + + [TestMethod] + public async Task TestSinglePartitionKeyContainer_Direct_MultiPartition() + { + await this.ExecuteTest( + SinglePartitionKeyDefinition, + ConnectionModes.Direct, + CollectionTypes.MultiPartition); + } + + [TestMethod] + public async Task TestSinglePartitionKeyContainer_Gateway_MultiPartition() + { + await this.ExecuteTest( + SinglePartitionKeyDefinition, + ConnectionModes.Gateway, + CollectionTypes.MultiPartition); + } + + [TestMethod] + public async Task TestHierarchicalPartitionKeyContainer_Direct_SinglePartition() + { + await this.ExecuteTest( + HierarchicalPartitionKeyDefinition, + ConnectionModes.Direct, + CollectionTypes.MultiPartition); + } + + [TestMethod] + public async Task TestHierarchicalPartitionKeyContainer_Gateway_SinglePartition() + { + await this.ExecuteTest( + HierarchicalPartitionKeyDefinition, + ConnectionModes.Gateway, + CollectionTypes.SinglePartition); + } + + [TestMethod] + public async Task TestHierarchicalPartitionKeyContainer_Direct_MultiPartition() + { + await this.ExecuteTest( + HierarchicalPartitionKeyDefinition, + ConnectionModes.Direct, + CollectionTypes.MultiPartition); + } + + [TestMethod] + public async Task TestHierarchicalPartitionKeyContainer_Gateway_MultiPartition() + { + await this.ExecuteTest( + HierarchicalPartitionKeyDefinition, + ConnectionModes.Gateway, + CollectionTypes.MultiPartition); + } + + private async Task ExecuteTest(PartitionKeyDefinition partitionKeyDefinition, ConnectionModes connectionMode, CollectionTypes collectionType) + { + await this.CreateIngestQueryDeleteAsync( + connectionMode, + collectionType, + SampleDocuments, + (container, documents) => this.ExecuteAllQueries(container, partitionKeyDefinition, documents), + partitionKeyDefinition, + IndexingPolicy); + } + + private static Cosmos.IndexingPolicy IndexingPolicy => + new Cosmos.IndexingPolicy + { + Automatic = true, + IndexingMode = Cosmos.IndexingMode.Consistent, + IncludedPaths = new Collection + { + new Cosmos.IncludedPath + { + Path = "/*" + } + } + }; + + private static string GetEpk(PartitionKeyDefinition partitionKeyDefinition, params string[] values) + { + if (partitionKeyDefinition == null) + { + throw new ArgumentNullException(nameof(partitionKeyDefinition)); + } + + if (values == null || values.Length == 0) + { + throw new ArgumentException("Values array must have at least one element", nameof(values)); + } + + if (values.Length > 3) + { + throw new ArgumentException("Values array cannot have more than 3 elements", nameof(values)); + } + + if (values.Length != partitionKeyDefinition.Paths.Count) + { + throw new ArgumentException( + $"Number of values ({values.Length}) must match number of partition key paths ({partitionKeyDefinition.Paths.Count})", + nameof(values)); + } + + // Build the partition key from the values + PartitionKeyBuilder pkBuilder = new PartitionKeyBuilder(); + + foreach (string value in values) + { + if (value == null) + { + pkBuilder.AddNullValue(); + } + else + { + pkBuilder.Add(value); + } + } + + Cosmos.PartitionKey pk = pkBuilder.Build(); + + string epk = pk.InternalKey.GetEffectivePartitionKeyString(partitionKeyDefinition); + + return epk; + } + + private async Task ExecuteAllQueries( + Container container, + PartitionKeyDefinition partitionKeyDefinition, + IReadOnlyList documents) + { + Console.WriteLine("\n=== Executing All Query Tests ===\n"); + + // Queries must project full partition key values from the documents hit by the query. + // The projected pk values are used to validate that only documents within the FeedRange are returned. + List queries = new List + { + @" + SELECT * + FROM c", + + @" + SELECT * + FROM c + WHERE STARTSWITH(c.tenant, 'tenant')", + + @" + SELECT * + FROM c + WHERE c.operation = 'create' OR c.operation = 'delete'", + + @" + SELECT + COUNT(1) count, + c.tenant, + c.session, + c.user + FROM c + GROUP BY c.tenant, c.session, c.user", + + @" + SELECT * + FROM c + ORDER BY c.tenant", + + @" + SELECT + min(c.duration) AS minDuration, + max(c.duration) AS maxDuration, + sum(c.duration) AS sumDuration, + c.tenant, + c.session, + c.user + FROM c + GROUP BY c.tenant, c.session, c.user + ORDER BY c.tenant" + }; + + // Step 1: Get distinct partition key values + Console.WriteLine("Step 1: Extracting distinct partition key values from documents..."); + List distinctPkValues = GetDistinctPartitionKeyValues(documents, partitionKeyDefinition); + Console.WriteLine($"Found {distinctPkValues.Count} distinct partition key value combinations\n"); + + // Step 2: Generate EPKs for each distinct partition key value + Console.WriteLine("Step 2: Generating EPK values..."); + List<(string[] pkValues, string epk)> epkList = GenerateEpksForPartitionKeys(distinctPkValues, partitionKeyDefinition); + Console.WriteLine($"Generated {epkList.Count} EPK values\n"); + + // Step 3: Sort EPKs and create ranges between consecutive values + Console.WriteLine("Step 3: Sorting EPK values and creating ranges..."); + List<(string[] pkValues, string epk)> sortedEpks = epkList.OrderBy(item => item.epk, StringComparer.Ordinal).ToList(); + Console.WriteLine($"Sorted {sortedEpks.Count} unique EPK values\n"); + + // Print sorted EPKs + foreach ((string[] pkValues, string epk) in sortedEpks) + { + Console.WriteLine($" EPK: {epk}, PK Values: [{string.Join(", ", pkValues)}]"); + } + + // Step 4: Create FeedRanges from each minEpk to all subsequent maxEpks and execute queries + for (int i = 0; i < sortedEpks.Count - 1; i++) + { + (string[] minPkValues, string minEpk) = sortedEpks[i]; + + // Nested loop: for each minEpk, test ranges to all subsequent maxEpks + for (int j = i; j < sortedEpks.Count; j++) + { + (string[] maxPkValues, string maxEpk) = sortedEpks[j]; + + Console.WriteLine($"\n====== Executing queries for EPK range [min:{i + 1}, max:{j + 1}] ======"); + Console.WriteLine($"Min EPK: {minEpk} (inclusive)"); + Console.WriteLine($"Max EPK: {maxEpk} (exclusive)"); + + // Print all PK values within the range (inclusive) + Console.WriteLine("PkValues: ["); + for (int pkIndex = i; pkIndex <= j; pkIndex++) + { + (string[] pkValues, string epk) = sortedEpks[pkIndex]; + Console.WriteLine($" [{string.Join(", ", pkValues)}]" + (pkIndex == j ? " (exclusive)" : "(inclusive),")); + } + Console.WriteLine(")"); + + // Create FeedRange from min to max EPK values + FeedRange feedRange = CreateFeedRangeFromEpkRange(minEpk, maxEpk); + Console.WriteLine($"FeedRange: {feedRange.ToJsonString()}\n"); + + for (int k = 0; k < queries.Count; k++) + { + string query = queries[k]; + Console.WriteLine("------------"); + Console.WriteLine($"Query {k + 1}:{query}\n"); + + FeedIterator iterator = container.GetItemQueryIterator( + feedRange, + new QueryDefinition(query)); + List results = new List(); + while (iterator.HasMoreResults) + { + FeedResponse response = await iterator.ReadNextAsync(); + results.AddRange(response); + } + + Console.WriteLine($"Results: {results.Count} documents:"); + foreach (CosmosElement result in results) + { + Console.WriteLine($" {result}"); + } + + // Validate EPK ranges + await this.ValidateResultsWithinFeedRange(results, feedRange, partitionKeyDefinition); + Console.WriteLine("------------\n"); + } + } + } + + Console.WriteLine("\n=== All Queries Completed ===\n"); + } + + private static List GetDistinctPartitionKeyValues( + IReadOnlyList documents, + PartitionKeyDefinition partitionKeyDefinition) + { + HashSet distinctValuesSet = new HashSet(); + List distinctValuesList = new List(); + int pathCount = partitionKeyDefinition.Paths.Count; + + foreach (CosmosObject document in documents) + { + string[] pkValues = ExtractPartitionKeyValues(document, partitionKeyDefinition); + + if (pathCount == 1) + { + // Single partition key: add the single value + string key = string.Join("|", pkValues.Select(v => v ?? "<>")); + if (distinctValuesSet.Add(key)) + { + distinctValuesList.Add(pkValues); + } + } + else + { + // Hierarchical partition key: generate all prefixes (1, 2, and 3 elements) + for (int prefixLength = 1; prefixLength <= pathCount; prefixLength++) + { + string[] prefixValues = pkValues.Take(prefixLength).ToArray(); + string key = string.Join("|", prefixValues.Select(v => v ?? "<>")); + + if (distinctValuesSet.Add(key)) + { + distinctValuesList.Add(prefixValues); + } + } + } + } + + return distinctValuesList; + } + + private static List<(string[] pkValues, string epk)> GenerateEpksForPartitionKeys( + List distinctPkValues, + PartitionKeyDefinition partitionKeyDefinition) + { + List<(string[] pkValues, string epk)> epkList = new List<(string[], string)>(); + + foreach (string[] pkValues in distinctPkValues) + { + // Create a partition key definition matching the number of values + PartitionKeyDefinition pkDef = new PartitionKeyDefinition + { + Paths = new Collection(partitionKeyDefinition.Paths.Take(pkValues.Length).ToList()), + Kind = partitionKeyDefinition.Kind, + Version = partitionKeyDefinition.Version + }; + + string epk = GetEpk(pkDef, pkValues); + epkList.Add((pkValues, epk)); + } + + return epkList; + } + + private static FeedRange CreateFeedRangeFromEpkRange(string minEpk, string maxEpk) + { + string feedRangeSerialization = Newtonsoft.Json.JsonConvert.SerializeObject(new + { + Range = new { min = minEpk, max = maxEpk } + }); + + return FeedRange.FromJsonString(feedRangeSerialization); + } + + private async Task ValidateResultsWithinFeedRange( + List results, + FeedRange feedRange, + PartitionKeyDefinition partitionKeyDefinition) + { + // Get the EPK ranges from the FeedRange + IReadOnlyList> epkRanges = await ((FeedRangeInternal)feedRange).GetEffectiveRangesAsync( + await this.Client.DocumentClient.GetPartitionKeyRangeCacheAsync(Cosmos.Tracing.NoOpTrace.Singleton), + null, + partitionKeyDefinition, + Cosmos.Tracing.NoOpTrace.Singleton); + + int validCount = 0; + int invalidCount = 0; + + foreach (CosmosElement result in results) + { + if (result is CosmosObject cosmosObject) + { + // Extract partition key values from the result + string[] pkValues = ExtractPartitionKeyValues(cosmosObject, partitionKeyDefinition); + + if (pkValues != null) + { + // Get the EPK for this document + string documentEpk = GetEpk(partitionKeyDefinition, pkValues); + + // Check if the EPK is within any of the FeedRange EPK ranges + bool isWithinRange = false; + foreach (Documents.Routing.Range epkRange in epkRanges) + { + if (IsEpkWithinRange(documentEpk, epkRange)) + { + isWithinRange = true; + break; + } + } + + if (isWithinRange) + { + validCount++; + } + else + { + invalidCount++; + Console.WriteLine($" WARNING: Document with EPK '{documentEpk}' is outside FeedRange"); + } + } + } + } + + Console.WriteLine($"\nValidation: {validCount} documents within range, {invalidCount} documents outside range"); + + if (invalidCount > 0) + { + Assert.Fail($"Found {invalidCount} documents outside the specified FeedRange"); + } + } + + private static string[] ExtractPartitionKeyValues(CosmosObject cosmosObject, PartitionKeyDefinition partitionKeyDefinition) + { + string[] values = new string[partitionKeyDefinition.Paths.Count]; + + for (int i = 0; i < partitionKeyDefinition.Paths.Count; i++) + { + string path = partitionKeyDefinition.Paths[i]; + string propertyName = path.TrimStart('/'); + + if (cosmosObject.TryGetValue(propertyName, out CosmosElement element)) + { + if (element is CosmosString cosmosString) + { + values[i] = cosmosString.Value; + } + else if (element is CosmosNull) + { + values[i] = null; + } + else + { + // For other types, convert to string representation + values[i] = element.ToString(); + } + } + else + { + values[i] = null; + } + } + + return values; + } + + private static bool IsEpkWithinRange(string epk, Documents.Routing.Range range) + { + // Check if epk is >= or > range.Min depending on IsMinInclusive + int minComparisonValue = string.Compare(epk, range.Min, StringComparison.Ordinal); + bool isMinWithinRange = range.IsMinInclusive ? (minComparisonValue >= 0) : (minComparisonValue > 0); + + // Check if epk is < or <= range.Max depending on IsMaxInclusive + int maxComparisonValue = string.Compare(epk, range.Max, StringComparison.Ordinal); + bool isMaxWithinRange = range.IsMaxInclusive ? (maxComparisonValue <= 0) : (maxComparisonValue < 0); + + return isMinWithinRange && isMaxWithinRange; + } + } +} + diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/QueryTestsBase.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/QueryTestsBase.cs index 9e55052dcb..6d601a784d 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/QueryTestsBase.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/QueryTestsBase.cs @@ -1,382 +1,388 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- -namespace Microsoft.Azure.Cosmos.EmulatorTests.Query -{ - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Linq; - using System.Net; - using System.Runtime.ExceptionServices; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Cosmos.SDK.EmulatorTests; - using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Documents; - using Microsoft.Azure.Documents.Routing; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - - /// - /// Tests for CrossPartitionQueryTests. - /// - [Microsoft.Azure.Cosmos.SDK.EmulatorTests.TestClass] - [TestCategory("Query")] - public abstract class QueryTestsBase - { - internal static readonly string[] NoDocuments = new string[] { }; - internal RequestChargeTrackingHandler GatewayRequestChargeHandler = new RequestChargeTrackingHandler(); - internal RequestChargeTrackingHandler DirectRequestChargeHandler = new RequestChargeTrackingHandler(); - internal CosmosClient GatewayClient; - internal CosmosClient Client; - internal Cosmos.Database database; - - [FlagsAttribute] - internal enum ConnectionModes - { - None = 0, - Direct = 0x1, - Gateway = 0x2, - } - - [FlagsAttribute] - internal enum CollectionTypes - { - None = 0, - NonPartitioned = 0x1, - SinglePartition = 0x2, - MultiPartition = 0x4, - } - - [TestInitialize] - public async Task Initialize() - { - this.GatewayClient = TestCommon.CreateCosmosClient(true, builder => builder.AddCustomHandlers(this.GatewayRequestChargeHandler)); - this.Client = TestCommon.CreateCosmosClient(false, builder => builder.AddCustomHandlers(this.DirectRequestChargeHandler)); - this.database = await this.Client.CreateDatabaseAsync(Guid.NewGuid().ToString() + "db"); - } - - [TestCleanup] - public async Task Cleanup() - { - await this.database.DeleteStreamAsync(); - this.Client.Dispose(); - this.GatewayClient.Dispose(); - } - - private static string GetApiVersion() - { - return HttpConstants.Versions.CurrentVersion; - } - - private static void SetApiVersion(string apiVersion) - { - HttpConstants.Versions.CurrentVersion = apiVersion; - HttpConstants.Versions.CurrentVersionUTF8 = Encoding.UTF8.GetBytes(apiVersion); - } - - private async Task> GetPartitionKeyRanges(ContainerProperties container) - { - Range fullRange = new Range( - PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, - PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, - true, - false); - IRoutingMapProvider routingMapProvider = await this.Client.DocumentClient.GetPartitionKeyRangeCacheAsync(NoOpTrace.Singleton); - Assert.IsNotNull(routingMapProvider); - - IReadOnlyList ranges = await routingMapProvider.TryGetOverlappingRangesAsync( - container.ResourceId, - fullRange, - NoOpTrace.Singleton); - return ranges; - } - - private async Task CreateMultiPartitionContainer( - string partitionKey, - Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy, +//----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//----------------------------------------------------------------------- +namespace Microsoft.Azure.Cosmos.EmulatorTests.Query +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Linq; + using System.Net; + using System.Runtime.ExceptionServices; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.CosmosElements; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.SDK.EmulatorTests; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Routing; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json; + using Newtonsoft.Json.Linq; + + /// + /// Tests for CrossPartitionQueryTests. + /// + [Microsoft.Azure.Cosmos.SDK.EmulatorTests.TestClass] + [TestCategory("Query")] + public abstract class QueryTestsBase + { + internal static readonly string[] NoDocuments = new string[] { }; + internal RequestChargeTrackingHandler GatewayRequestChargeHandler = new RequestChargeTrackingHandler(); + internal RequestChargeTrackingHandler DirectRequestChargeHandler = new RequestChargeTrackingHandler(); + internal CosmosClient GatewayClient; + internal CosmosClient Client; + internal Cosmos.Database database; + + [FlagsAttribute] + internal enum ConnectionModes + { + None = 0, + Direct = 0x1, + Gateway = 0x2, + } + + [FlagsAttribute] + internal enum CollectionTypes + { + None = 0, + NonPartitioned = 0x1, + SinglePartition = 0x2, + MultiPartition = 0x4, + } + + [TestInitialize] + public async Task Initialize() + { + this.GatewayClient = TestCommon.CreateCosmosClient(true, builder => builder.AddCustomHandlers(this.GatewayRequestChargeHandler)); + this.Client = TestCommon.CreateCosmosClient(false, builder => builder.AddCustomHandlers(this.DirectRequestChargeHandler)); + this.database = await this.Client.CreateDatabaseAsync(Guid.NewGuid().ToString() + "db"); + } + + [TestCleanup] + public async Task Cleanup() + { + await this.database.DeleteStreamAsync(); + this.Client.Dispose(); + this.GatewayClient.Dispose(); + } + + private static string GetApiVersion() + { + return HttpConstants.Versions.CurrentVersion; + } + + private static void SetApiVersion(string apiVersion) + { + HttpConstants.Versions.CurrentVersion = apiVersion; + HttpConstants.Versions.CurrentVersionUTF8 = Encoding.UTF8.GetBytes(apiVersion); + } + + private async Task> GetPartitionKeyRanges(ContainerProperties container) + { + Range fullRange = new Range( + PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, + PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, + true, + false); + IRoutingMapProvider routingMapProvider = await this.Client.DocumentClient.GetPartitionKeyRangeCacheAsync(NoOpTrace.Singleton); + Assert.IsNotNull(routingMapProvider); + + IReadOnlyList ranges = await routingMapProvider.TryGetOverlappingRangesAsync( + container.ResourceId, + fullRange, + NoOpTrace.Singleton); + return ranges; + } + + private async Task CreateMultiPartitionContainer( + PartitionKeyDefinition partitionKey, + Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy, Cosmos.GeospatialType geospatialType, - Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) - { - ContainerResponse containerResponse = await this.CreatePartitionedContainer( - throughput: 25000, - partitionKey: partitionKey, - indexingPolicy: indexingPolicy, + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) + { + ContainerResponse containerResponse = await this.CreatePartitionedContainer( + throughput: 25000, + partitionKey: partitionKey, + indexingPolicy: indexingPolicy, geospatialType, - vectorEmbeddingPolicy); - - IReadOnlyList ranges = await this.GetPartitionKeyRanges(containerResponse); - Assert.IsTrue( - ranges.Count() > 1, - $"{nameof(CreateMultiPartitionContainer)} failed to create a container with more than 1 physical partition."); - - return containerResponse; - } - - private async Task CreateSinglePartitionContainer( - string partitionKey, - Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy, + vectorEmbeddingPolicy); + + IReadOnlyList ranges = await this.GetPartitionKeyRanges(containerResponse); + Assert.IsTrue( + ranges.Count() > 1, + $"{nameof(CreateMultiPartitionContainer)} failed to create a container with more than 1 physical partition."); + + return containerResponse; + } + + private async Task CreateSinglePartitionContainer( + PartitionKeyDefinition partitionKey, + Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy, Cosmos.GeospatialType geospatialType, - Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) - { - ContainerResponse containerResponse = await this.CreatePartitionedContainer( - throughput: 4000, - partitionKey: partitionKey, - indexingPolicy: indexingPolicy, + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) + { + ContainerResponse containerResponse = await this.CreatePartitionedContainer( + throughput: 4000, + partitionKey: partitionKey, + indexingPolicy: indexingPolicy, geospatialType: geospatialType, - vectorEmbeddingPolicy: vectorEmbeddingPolicy); - - Assert.IsNotNull(containerResponse); - Assert.AreEqual(HttpStatusCode.Created, containerResponse.StatusCode); - Assert.IsNotNull(containerResponse.Resource); - Assert.IsNotNull(containerResponse.Resource.ResourceId); - - IReadOnlyList ranges = await this.GetPartitionKeyRanges(containerResponse); - Assert.AreEqual(1, ranges.Count()); - - return containerResponse; - } - - private async Task CreateNonPartitionedContainerAsync( - Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy = null, - Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) - { - string containerName = Guid.NewGuid().ToString() + "container"; - await NonPartitionedContainerHelper.CreateNonPartitionedContainer( - this.database, - containerName, - indexingPolicy == null ? null : JsonConvert.SerializeObject(indexingPolicy), - geospatialType); - - return this.database.GetContainer(containerName); - } - - private async Task CreatePartitionedContainer( - int throughput, - string partitionKey, - Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy, + vectorEmbeddingPolicy: vectorEmbeddingPolicy); + + Assert.IsNotNull(containerResponse); + Assert.AreEqual(HttpStatusCode.Created, containerResponse.StatusCode); + Assert.IsNotNull(containerResponse.Resource); + Assert.IsNotNull(containerResponse.Resource.ResourceId); + + IReadOnlyList ranges = await this.GetPartitionKeyRanges(containerResponse); + Assert.AreEqual(1, ranges.Count()); + + return containerResponse; + } + + private async Task CreateNonPartitionedContainerAsync( + Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) + { + string containerName = Guid.NewGuid().ToString() + "container"; + await NonPartitionedContainerHelper.CreateNonPartitionedContainer( + this.database, + containerName, + indexingPolicy == null ? null : JsonConvert.SerializeObject(indexingPolicy), + geospatialType); + + return this.database.GetContainer(containerName); + } + + private async Task CreatePartitionedContainer( + int throughput, + PartitionKeyDefinition partitionKey, + Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy, Cosmos.GeospatialType geospatialType, - Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) - { - // Assert that database exists (race deletes are possible when used concurrently) - ResponseMessage responseMessage = await this.database.ReadStreamAsync(); - Assert.AreEqual(HttpStatusCode.OK, responseMessage.StatusCode); - - ContainerResponse containerResponse = await this.database.CreateContainerAsync( - new ContainerProperties - { - Id = Guid.NewGuid().ToString() + "container", - IndexingPolicy = indexingPolicy ?? new Cosmos.IndexingPolicy - { - IncludedPaths = new Collection - { - new Cosmos.IncludedPath - { - Path = "/*", - Indexes = new Collection - { - Cosmos.Index.Range(Cosmos.DataType.Number), - Cosmos.Index.Range(Cosmos.DataType.String), - } - } - } - }, - PartitionKey = partitionKey == null ? null : new PartitionKeyDefinition - { - Paths = new Collection { partitionKey }, - Kind = PartitionKind.Hash - }, + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) + { + // Assert that database exists (race deletes are possible when used concurrently) + ResponseMessage responseMessage = await this.database.ReadStreamAsync(); + Assert.AreEqual(HttpStatusCode.OK, responseMessage.StatusCode); + + ContainerResponse containerResponse = await this.database.CreateContainerAsync( + new ContainerProperties + { + Id = Guid.NewGuid().ToString() + "container", + IndexingPolicy = indexingPolicy ?? new Cosmos.IndexingPolicy + { + IncludedPaths = new Collection + { + new Cosmos.IncludedPath + { + Path = "/*", + Indexes = new Collection + { + Cosmos.Index.Range(Cosmos.DataType.Number), + Cosmos.Index.Range(Cosmos.DataType.String), + } + } + } + }, + PartitionKey = partitionKey, GeospatialConfig = new Cosmos.GeospatialConfig(geospatialType), - VectorEmbeddingPolicy = vectorEmbeddingPolicy - }, - // This throughput needs to be about half the max with multi master - // otherwise it will create about twice as many partitions. - throughput); - - Assert.IsNotNull(containerResponse); - Assert.AreEqual(HttpStatusCode.Created, containerResponse.StatusCode); - Assert.IsNotNull(containerResponse.Resource); - Assert.IsNotNull(containerResponse.Resource.ResourceId); - - return containerResponse; - } - - private Task<(Container, IReadOnlyList)> CreateNonPartitionedContainerAndIngestDocumentsAsync( - IEnumerable documents, - Cosmos.IndexingPolicy indexingPolicy, - Cosmos.GeospatialType geospatialType) - { - return this.CreateContainerAndIngestDocumentsAsync( - CollectionTypes.NonPartitioned, - documents, - partitionKey: null, - indexingPolicy: indexingPolicy, + VectorEmbeddingPolicy = vectorEmbeddingPolicy + }, + // This throughput needs to be about half the max with multi master + // otherwise it will create about twice as many partitions. + throughput); + + Assert.IsNotNull(containerResponse); + Assert.AreEqual(HttpStatusCode.Created, containerResponse.StatusCode); + Assert.IsNotNull(containerResponse.Resource); + Assert.IsNotNull(containerResponse.Resource.ResourceId); + + return containerResponse; + } + + private Task<(Container, IReadOnlyList)> CreateNonPartitionedContainerAndIngestDocumentsAsync( + IEnumerable documents, + Cosmos.IndexingPolicy indexingPolicy, + Cosmos.GeospatialType geospatialType) + { + return this.CreateContainerAndIngestDocumentsAsync( + CollectionTypes.NonPartitioned, + documents, + partitionKey: null, + indexingPolicy: indexingPolicy, geospatialType: geospatialType, - vectorEmbeddingPolicy: null); - } - - private Task<(Container, IReadOnlyList)> CreateSinglePartitionContainerAndIngestDocumentsAsync( - IEnumerable documents, - string partitionKey, - Cosmos.IndexingPolicy indexingPolicy, + vectorEmbeddingPolicy: null); + } + + private Task<(Container, IReadOnlyList)> CreateSinglePartitionContainerAndIngestDocumentsAsync( + IEnumerable documents, + PartitionKeyDefinition partitionKey, + Cosmos.IndexingPolicy indexingPolicy, Cosmos.GeospatialType geospatialType, - Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) - { - return this.CreateContainerAndIngestDocumentsAsync( - CollectionTypes.SinglePartition, - documents, - partitionKey, - indexingPolicy, + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) + { + return this.CreateContainerAndIngestDocumentsAsync( + CollectionTypes.SinglePartition, + documents, + partitionKey, + indexingPolicy, geospatialType, - vectorEmbeddingPolicy); - } - - private Task<(Container, IReadOnlyList)> CreateMultiPartitionContainerAndIngestDocumentsAsync( - IEnumerable documents, - string partitionKey, - Cosmos.IndexingPolicy indexingPolicy, + vectorEmbeddingPolicy); + } + + private Task<(Container, IReadOnlyList)> CreateMultiPartitionContainerAndIngestDocumentsAsync( + IEnumerable documents, + PartitionKeyDefinition partitionKey, + Cosmos.IndexingPolicy indexingPolicy, Cosmos.GeospatialType geospatialType, - Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) - { - return this.CreateContainerAndIngestDocumentsAsync( - CollectionTypes.MultiPartition, - documents, - partitionKey, - indexingPolicy, + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) + { + return this.CreateContainerAndIngestDocumentsAsync( + CollectionTypes.MultiPartition, + documents, + partitionKey, + indexingPolicy, geospatialType, - vectorEmbeddingPolicy); - } - - private async Task<(Container, IReadOnlyList)> CreateContainerAndIngestDocumentsAsync( - CollectionTypes collectionType, - IEnumerable documents, - string partitionKey, - Cosmos.IndexingPolicy indexingPolicy, + vectorEmbeddingPolicy); + } + + private async Task<(Container, IReadOnlyList)> CreateContainerAndIngestDocumentsAsync( + CollectionTypes collectionType, + IEnumerable documents, + PartitionKeyDefinition partitionKey, + Cosmos.IndexingPolicy indexingPolicy, Cosmos.GeospatialType geospatialType, - Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) - { - Container container = collectionType switch - { - CollectionTypes.NonPartitioned => await this.CreateNonPartitionedContainerAsync(indexingPolicy, geospatialType), - CollectionTypes.SinglePartition => await this.CreateSinglePartitionContainer(partitionKey, indexingPolicy, geospatialType, vectorEmbeddingPolicy), - CollectionTypes.MultiPartition => await this.CreateMultiPartitionContainer(partitionKey, indexingPolicy, geospatialType, vectorEmbeddingPolicy), - _ => throw new ArgumentException($"Unknown {nameof(CollectionTypes)} : {collectionType}"), - }; - List insertedDocuments = new List(); - foreach (string document in documents) - { - JObject documentObject = JsonConvert.DeserializeObject(document); - // Add an id - if (documentObject["id"] == null) - { - documentObject["id"] = Guid.NewGuid().ToString(); - } - - // Get partition key value. - Cosmos.PartitionKey pkValue; - if (partitionKey != null) - { - string jObjectPartitionKey = partitionKey.Remove(0, 1); - JValue pkToken = (JValue)documentObject[jObjectPartitionKey]; - if (pkToken == null) - { - pkValue = Cosmos.PartitionKey.None; - } - else - { - switch (pkToken.Type) - { - case JTokenType.Integer: - case JTokenType.Float: - pkValue = new Cosmos.PartitionKey(pkToken.Value()); - break; - case JTokenType.String: - pkValue = new Cosmos.PartitionKey(pkToken.Value()); - break; - case JTokenType.Boolean: - pkValue = new Cosmos.PartitionKey(pkToken.Value()); - break; - case JTokenType.Null: - pkValue = Cosmos.PartitionKey.Null; - break; - default: - throw new ArgumentException("Unknown partition key type"); - } - } - } - else - { - pkValue = Cosmos.PartitionKey.None; - } - - JObject createdDocument = await container.CreateItemAsync(documentObject, pkValue); - CosmosObject insertedDocument = CosmosObject.Parse(createdDocument.ToString()); - insertedDocuments.Add(insertedDocument); - } - - return (container, insertedDocuments); - } - - private static async Task CleanUp(CosmosClient client) - { - using (FeedIterator allDatabases = client.GetDatabaseQueryIterator()) - { - while (allDatabases.HasMoreResults) - { - foreach (DatabaseProperties db in await allDatabases.ReadNextAsync()) - { - await client.GetDatabase(db.Id).DeleteAsync(); - } - } - } - } - - internal async Task RunWithApiVersion(string apiVersion, Func function) - { - string originalApiVersion = GetApiVersion(); - CosmosClient originalCosmosClient = this.Client; - CosmosClient originalGatewayClient = this.GatewayClient; - Cosmos.Database originalDatabase = this.database; - - try - { - SetApiVersion(apiVersion); - if (apiVersion != originalApiVersion) - { - this.Client = TestCommon.CreateCosmosClient(false); - this.GatewayClient = TestCommon.CreateCosmosClient(true); - this.database = this.Client.GetDatabase(this.database.Id); - } - - await function(); - } - finally - { - this.Client.Dispose(); - this.GatewayClient.Dispose(); - this.Client = originalCosmosClient; - this.GatewayClient = originalGatewayClient; - this.database = originalDatabase; - SetApiVersion(originalApiVersion); - } - } - - internal delegate Task Query( - Container container, - IReadOnlyList documents); - - internal delegate Task Query( - Container container, - IReadOnlyList documents, - T testArgs); - + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy) + { + Container container = collectionType switch + { + CollectionTypes.NonPartitioned => await this.CreateNonPartitionedContainerAsync(indexingPolicy, geospatialType), + CollectionTypes.SinglePartition => await this.CreateSinglePartitionContainer(partitionKey, indexingPolicy, geospatialType, vectorEmbeddingPolicy), + CollectionTypes.MultiPartition => await this.CreateMultiPartitionContainer(partitionKey, indexingPolicy, geospatialType, vectorEmbeddingPolicy), + _ => throw new ArgumentException($"Unknown {nameof(CollectionTypes)} : {collectionType}"), + }; + List insertedDocuments = new List(); + foreach (string document in documents) + { + JObject documentObject = JsonConvert.DeserializeObject(document); + if (documentObject["id"] == null) + { + // Add an id + documentObject["id"] = Guid.NewGuid().ToString(); + } + + + // Get partition key value. + Cosmos.PartitionKey pkValue; + if (partitionKey != null) + { + PartitionKeyBuilder partitionKeyBuilder = new PartitionKeyBuilder(); + + foreach (string path in partitionKey.Paths) + { + List propertyNames = path.Split('/', StringSplitOptions.RemoveEmptyEntries).ToList(); + string jsonPath = "$." + string.Join(".", propertyNames); + JToken pkToken = documentObject.SelectToken(jsonPath); + + if (pkToken == null) + { + partitionKeyBuilder.AddNoneType(); + } + else + { + switch (pkToken.Type) + { + case JTokenType.Integer: + case JTokenType.Float: + partitionKeyBuilder.Add(pkToken.Value()); + break; + case JTokenType.String: + partitionKeyBuilder.Add(pkToken.Value()); + break; + case JTokenType.Boolean: + partitionKeyBuilder.Add(pkToken.Value()); + break; + case JTokenType.Null: + partitionKeyBuilder.AddNullValue(); + break; + default: + throw new ArgumentException("Unknown partition key type"); + } + } + } + + pkValue = partitionKeyBuilder.Build(); + } + else + { + pkValue = Cosmos.PartitionKey.None; + } + + JObject createdDocument = await container.CreateItemAsync(documentObject, pkValue); + CosmosObject insertedDocument = CosmosObject.Parse(createdDocument.ToString()); + insertedDocuments.Add(insertedDocument); + } + + return (container, insertedDocuments); + } + + private static async Task CleanUp(CosmosClient client) + { + using (FeedIterator allDatabases = client.GetDatabaseQueryIterator()) + { + while (allDatabases.HasMoreResults) + { + foreach (DatabaseProperties db in await allDatabases.ReadNextAsync()) + { + await client.GetDatabase(db.Id).DeleteAsync(); + } + } + } + } + + internal async Task RunWithApiVersion(string apiVersion, Func function) + { + string originalApiVersion = GetApiVersion(); + CosmosClient originalCosmosClient = this.Client; + CosmosClient originalGatewayClient = this.GatewayClient; + Cosmos.Database originalDatabase = this.database; + + try + { + SetApiVersion(apiVersion); + if (apiVersion != originalApiVersion) + { + this.Client = TestCommon.CreateCosmosClient(false); + this.GatewayClient = TestCommon.CreateCosmosClient(true); + this.database = this.Client.GetDatabase(this.database.Id); + } + + await function(); + } + finally + { + this.Client.Dispose(); + this.GatewayClient.Dispose(); + this.Client = originalCosmosClient; + this.GatewayClient = originalGatewayClient; + this.database = originalDatabase; + SetApiVersion(originalApiVersion); + } + } + + internal delegate Task Query( + Container container, + IReadOnlyList documents); + + internal delegate Task Query( + Container container, + IReadOnlyList documents, + T testArgs); + internal delegate CosmosClient CosmosClientFactory(ConnectionMode connectionMode); internal static ConnectionModes ToTestConnectionMode(ConnectionMode connectionMode) @@ -387,568 +393,627 @@ internal static ConnectionModes ToTestConnectionMode(ConnectionMode connectionMo ConnectionMode.Gateway => ConnectionModes.Gateway, _ => throw new ArgumentOutOfRangeException(nameof(connectionMode), connectionMode, null) }; - } - - internal Task CreateIngestQueryDeleteAsync( - ConnectionModes connectionModes, - CollectionTypes collectionTypes, - IEnumerable documents, - Query query, - string partitionKey = "/id", - Cosmos.IndexingPolicy indexingPolicy = null, - CosmosClientFactory cosmosClientFactory = null, + } + + internal Task CreateIngestQueryDeleteAsync( + ConnectionModes connectionModes, + CollectionTypes collectionTypes, + IEnumerable documents, + Query query, + string partitionKey = "/id", + Cosmos.IndexingPolicy indexingPolicy = null, + CosmosClientFactory cosmosClientFactory = null, Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography, - Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy = null) - { - Task queryWrapper(Container container, IReadOnlyList inputDocuments, object throwaway) - { - return query(container, inputDocuments); - } - - return this.CreateIngestQueryDeleteAsync( - connectionModes, - collectionTypes, - documents, - queryWrapper, - null, - partitionKey, - indexingPolicy, - cosmosClientFactory, + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy = null) + { + Task queryWrapper(Container container, IReadOnlyList inputDocuments, object throwaway) + { + return query(container, inputDocuments); + } + + return this.CreateIngestQueryDeleteAsync( + connectionModes, + collectionTypes, + documents, + queryWrapper, + null, + partitionKey, + indexingPolicy, + cosmosClientFactory, geospatialType, - vectorEmbeddingPolicy); - } - - internal Task CreateIngestQueryDeleteAsync( - ConnectionModes connectionModes, - CollectionTypes collectionTypes, - IEnumerable documents, - Query query, - T testArgs, - string partitionKey = "/id", - Cosmos.IndexingPolicy indexingPolicy = null, - CosmosClientFactory cosmosClientFactory = null, + vectorEmbeddingPolicy); + } + + internal Task CreateIngestQueryDeleteAsync( + ConnectionModes connectionModes, + CollectionTypes collectionTypes, + IEnumerable documents, + Query query, + PartitionKeyDefinition partitionKeyDefinition, + Cosmos.IndexingPolicy indexingPolicy = null, + CosmosClientFactory cosmosClientFactory = null, Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography, - Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy = null) - { - return this.CreateIngestQueryDeleteAsync( - connectionModes, - collectionTypes, - documents, - query, - cosmosClientFactory ?? this.CreateDefaultCosmosClient, - testArgs, - partitionKey, - indexingPolicy, + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy = null) + { + Task queryWrapper(Container container, IReadOnlyList inputDocuments, object throwaway) + { + return query(container, inputDocuments); + } + + return this.CreateIngestQueryDeleteAsync( + connectionModes, + collectionTypes, + documents, + queryWrapper, + null, + partitionKeyDefinition, + indexingPolicy, + cosmosClientFactory, geospatialType, - vectorEmbeddingPolicy); - } - - /// - /// Task that wraps boiler plate code for query tests (container create -> ingest documents -> query documents -> delete collections). - /// Note that this function will take the cross product connectionModes - /// - /// The connection modes to use. - /// The documents to ingest - /// - /// The callback for the queries. - /// All the standard arguments will be passed in. - /// Please make sure that this function is idempotent, since a container will be reused for each connection mode. - /// - /// - /// The callback for the create CosmosClient. This is invoked for the different ConnectionModes that the query is targeting. - /// If CosmosClient instantiated by this does not apply the expected ConnectionMode, an assert is thrown. - /// - /// The partition key for the partition container. - /// The optional args that you want passed in to the query. - /// A task to await on. - internal async Task CreateIngestQueryDeleteAsync( - ConnectionModes connectionModes, - CollectionTypes collectionTypes, - IEnumerable documents, - Query query, - CosmosClientFactory cosmosClientFactory, - T testArgs, - string partitionKey = "/id", - Cosmos.IndexingPolicy indexingPolicy = null, + vectorEmbeddingPolicy); + } + + internal Task CreateIngestQueryDeleteAsync( + ConnectionModes connectionModes, + CollectionTypes collectionTypes, + IEnumerable documents, + Query query, + T testArgs, + string partitionKey = "/id", + Cosmos.IndexingPolicy indexingPolicy = null, + CosmosClientFactory cosmosClientFactory = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography, + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy = null) + { + PartitionKeyDefinition partitionKeyDefinition = new PartitionKeyDefinition() + { + Paths = new Collection() { partitionKey } + }; + + return this.CreateIngestQueryDeleteAsync( + connectionModes, + collectionTypes, + documents, + query, + cosmosClientFactory ?? this.CreateDefaultCosmosClient, + testArgs, + partitionKeyDefinition, + indexingPolicy, + geospatialType, + vectorEmbeddingPolicy); + } + + internal Task CreateIngestQueryDeleteAsync( + ConnectionModes connectionModes, + CollectionTypes collectionTypes, + IEnumerable documents, + Query query, + T testArgs, + PartitionKeyDefinition partitionKeyDefinition, + Cosmos.IndexingPolicy indexingPolicy = null, + CosmosClientFactory cosmosClientFactory = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography, + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy = null) + { + return this.CreateIngestQueryDeleteAsync( + connectionModes, + collectionTypes, + documents, + query, + cosmosClientFactory ?? this.CreateDefaultCosmosClient, + testArgs, + partitionKeyDefinition, + indexingPolicy, + geospatialType, + vectorEmbeddingPolicy); + } + + /// + /// Task that wraps boiler plate code for query tests (container create -> ingest documents -> query documents -> delete collections). + /// Note that this function will take the cross product connectionModes + /// + /// The connection modes to use. + /// The documents to ingest + /// + /// The callback for the queries. + /// All the standard arguments will be passed in. + /// Please make sure that this function is idempotent, since a container will be reused for each connection mode. + /// + /// + /// The callback for the create CosmosClient. This is invoked for the different ConnectionModes that the query is targeting. + /// If CosmosClient instantiated by this does not apply the expected ConnectionMode, an assert is thrown. + /// + /// The partition key for the partition container. + /// The optional args that you want passed in to the query. + /// A task to await on. + internal async Task CreateIngestQueryDeleteAsync( + ConnectionModes connectionModes, + CollectionTypes collectionTypes, + IEnumerable documents, + Query query, + CosmosClientFactory cosmosClientFactory, + T testArgs, + PartitionKeyDefinition partitionKey, + Cosmos.IndexingPolicy indexingPolicy = null, Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography, - Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy = null) - { - try - { - IList<(Container, IReadOnlyList)> collectionsAndDocuments = new List<(Container, IReadOnlyList)>(); - foreach (CollectionTypes collectionType in Enum.GetValues(collectionTypes.GetType()).Cast().Where(collectionTypes.HasFlag)) - { - if (collectionType == CollectionTypes.None) - { - continue; - } - - Task<(Container, IReadOnlyList)> createContainerTask = collectionType switch - { - CollectionTypes.NonPartitioned => this.CreateNonPartitionedContainerAndIngestDocumentsAsync( - documents, - indexingPolicy, - geospatialType), - CollectionTypes.SinglePartition => this.CreateSinglePartitionContainerAndIngestDocumentsAsync( - documents, - partitionKey, - indexingPolicy, + Cosmos.VectorEmbeddingPolicy vectorEmbeddingPolicy = null) + { + try + { + IList<(Container, IReadOnlyList)> collectionsAndDocuments = new List<(Container, IReadOnlyList)>(); + foreach (CollectionTypes collectionType in Enum.GetValues(collectionTypes.GetType()).Cast().Where(collectionTypes.HasFlag)) + { + if (collectionType == CollectionTypes.None) + { + continue; + } + + Task<(Container, IReadOnlyList)> createContainerTask = collectionType switch + { + CollectionTypes.NonPartitioned => this.CreateNonPartitionedContainerAndIngestDocumentsAsync( + documents, + indexingPolicy, + geospatialType), + CollectionTypes.SinglePartition => this.CreateSinglePartitionContainerAndIngestDocumentsAsync( + documents, + partitionKey, + indexingPolicy, geospatialType, - vectorEmbeddingPolicy), - CollectionTypes.MultiPartition => this.CreateMultiPartitionContainerAndIngestDocumentsAsync( - documents, - partitionKey, - indexingPolicy, + vectorEmbeddingPolicy), + CollectionTypes.MultiPartition => this.CreateMultiPartitionContainerAndIngestDocumentsAsync( + documents, + partitionKey, + indexingPolicy, geospatialType, - vectorEmbeddingPolicy), - _ => throw new ArgumentException($"Unknown {nameof(CollectionTypes)} : {collectionType}"), - }; - collectionsAndDocuments.Add(await createContainerTask); - } - - List cosmosClients = new List(); - foreach (ConnectionModes connectionMode in Enum.GetValues(connectionModes.GetType()).Cast().Where(connectionModes.HasFlag)) - { - if (connectionMode == ConnectionModes.None) - { - continue; - } - - ConnectionMode targetConnectionMode = GetTargetConnectionMode(connectionMode); - CosmosClient cosmosClient = cosmosClientFactory(targetConnectionMode); - - Assert.AreEqual( - targetConnectionMode, - cosmosClient.ClientOptions.ConnectionMode, - "Test setup: Invalid connection policy applied to CosmosClient"); - cosmosClients.Add(cosmosClient); - } - - List queryTasks = new List(); - foreach (CosmosClient cosmosClient in cosmosClients) - { - foreach ((Container container, IReadOnlyList insertedDocuments) in collectionsAndDocuments) - { - Task queryTask = Task.Run(() => query(container, insertedDocuments, testArgs)); - queryTasks.Add(queryTask); - } - } - - await Task.WhenAll(queryTasks); - - List> deleteContainerTasks = new List>(); - foreach (Container container in collectionsAndDocuments.Select(tuple => tuple.Item1)) - { - deleteContainerTasks.Add(container.DeleteContainerAsync()); - } - - await Task.WhenAll(deleteContainerTasks); - } - catch (Exception ex) when (ex.GetType() != typeof(AssertFailedException)) - { - while (ex.InnerException != null) - { - ex = ex.InnerException; - } - - ExceptionDispatchInfo.Capture(ex).Throw(); - } - } - - private static ConnectionMode GetTargetConnectionMode(ConnectionModes connectionMode) - { - return connectionMode switch - { - ConnectionModes.Gateway => ConnectionMode.Gateway, - ConnectionModes.Direct => ConnectionMode.Direct, - _ => throw new ArgumentException($"Unexpected connection mode: {connectionMode}"), - }; - } - - internal CosmosClient CreateDefaultCosmosClient(ConnectionMode connectionMode) - { - return connectionMode switch - { - ConnectionMode.Gateway => this.GatewayClient, - ConnectionMode.Direct => this.Client, - _ => throw new ArgumentException($"Unexpected connection mode: {connectionMode}"), - }; - } - - internal CosmosClient CreateNewCosmosClient(ConnectionMode connectionMode) - { - return connectionMode switch - { - ConnectionMode.Gateway => TestCommon.CreateCosmosClient(true), - ConnectionMode.Direct => TestCommon.CreateCosmosClient(false), - _ => throw new ArgumentException($"Unexpected connection mode: {connectionMode}"), - }; - } - - internal static async Task> QueryWithContinuationTokensAsync( - Container container, - string query, - QueryRequestOptions queryRequestOptions = null) - { - if (queryRequestOptions == null) - { - queryRequestOptions = new QueryRequestOptions(); - } - - List resultsFromContinuationToken = new List(); - int resultCount = 0; - string continuationToken = null; - do - { - FeedIterator itemQuery = container.GetItemQueryIterator( - queryText: query, - requestOptions: queryRequestOptions, - continuationToken: continuationToken); - - try - { - while (true) - { - try - { - FeedResponse cosmosQueryResponse = await itemQuery.ReadNextAsync(); - if (queryRequestOptions.MaxItemCount.HasValue) - { - Assert.IsTrue( - cosmosQueryResponse.Count <= queryRequestOptions.MaxItemCount.Value, - $"Max Item Count is not being honored. Got {cosmosQueryResponse.Count} when {queryRequestOptions.MaxItemCount.Value} is the max."); - } - - resultsFromContinuationToken.AddRange(cosmosQueryResponse); - resultCount += cosmosQueryResponse.Count; - continuationToken = cosmosQueryResponse.ContinuationToken; - break; - } - catch (CosmosException cosmosException) when (cosmosException.StatusCode == (HttpStatusCode)429) - { - itemQuery.Dispose(); - itemQuery = container.GetItemQueryIterator( - queryText: query, - requestOptions: queryRequestOptions, - continuationToken: continuationToken); - } - } - } - finally - { - itemQuery.Dispose(); - } - } while (continuationToken != null); - - Assert.AreEqual(resultsFromContinuationToken.Count, resultCount); - return resultsFromContinuationToken; - } - - internal static async Task> QueryWithoutContinuationTokensAsync( - Container container, - string query, - QueryRequestOptions queryRequestOptions = null) - { - if (queryRequestOptions == null) - { - queryRequestOptions = new QueryRequestOptions(); - } - - int resultCount = 0; - List results = new List(); - FeedIterator itemQuery = container.GetItemQueryIterator( - queryText: query, - requestOptions: queryRequestOptions); - try - { - string continuationTokenForRetries = null; - while (itemQuery.HasMoreResults) - { - try - { - FeedResponse page = await itemQuery.ReadNextAsync(); - results.AddRange(page); - resultCount += page.Count; - - if (queryRequestOptions.MaxItemCount.HasValue) - { - if (page.Count > queryRequestOptions.MaxItemCount.Value) - { - Console.WriteLine(); - } - Assert.IsTrue( - page.Count <= queryRequestOptions.MaxItemCount.Value, - $"Max Item Count is not being honored. Got {page.Count} documents when the max is {queryRequestOptions.MaxItemCount.Value}."); - } - - try - { - continuationTokenForRetries = page.ContinuationToken; - } - catch (Exception) - { - // Grabbing a continuation token is not supported on all queries. - } - } - catch (CosmosException cosmosException) when (cosmosException.StatusCode == (HttpStatusCode)429) - { - itemQuery.Dispose(); - itemQuery = container.GetItemQueryIterator( - queryText: query, - requestOptions: queryRequestOptions, - continuationToken: continuationTokenForRetries); - - if (continuationTokenForRetries == null) - { - // The query failed and we don't have a save point, so just restart the whole thing. - results = new List(); - resultCount = 0; - } - } - } - } - finally - { - itemQuery.Dispose(); - } - - Assert.AreEqual(results.Count, resultCount); - return results; - } - - internal static async Task NoOp() - { - await Task.Delay(0); - } - - internal static Task> RunQueryAsync( - Container container, - string query, - QueryRequestOptions queryRequestOptions = null) - { - return RunQueryAsync(container, query, queryRequestOptions); - } - - internal static Task> RunQueryAsync( - Container container, - string query, - QueryRequestOptions queryRequestOptions = null) - { - return RunQueryCombinationsAsync( - container, - query, - queryRequestOptions, - QueryDrainingMode.ContinuationToken | QueryDrainingMode.HoldState); - } - - [Flags] - public enum QueryDrainingMode - { - None = 0, - HoldState = 1, - ContinuationToken = 2, - } - - internal static Task> RunQueryCombinationsAsync( - Container container, - string query, - QueryRequestOptions queryRequestOptions, - QueryDrainingMode queryDrainingMode) - { - return RunQueryCombinationsAsync(container, query, queryRequestOptions, queryDrainingMode); - } - - internal static async Task> RunQueryCombinationsAsync( - Container container, - string query, - QueryRequestOptions queryRequestOptions, - QueryDrainingMode queryDrainingMode) - { - if (queryDrainingMode == QueryDrainingMode.None) - { - throw new ArgumentOutOfRangeException(nameof(queryDrainingMode)); - } - - Dictionary> queryExecutionResults = new Dictionary>(); - - if (queryDrainingMode.HasFlag(QueryDrainingMode.HoldState)) - { - List queryResultsWithoutContinuationToken = await QueryWithoutContinuationTokensAsync( - container, - query, - queryRequestOptions); - - queryExecutionResults[QueryDrainingMode.HoldState] = queryResultsWithoutContinuationToken; - } - - if (queryDrainingMode.HasFlag(QueryDrainingMode.ContinuationToken)) - { - List queryResultsWithContinuationTokens = await QueryWithContinuationTokensAsync( - container, - query, - queryRequestOptions); - - queryExecutionResults[QueryDrainingMode.ContinuationToken] = queryResultsWithContinuationTokens; - } - - foreach (QueryDrainingMode queryDrainingMode1 in queryExecutionResults.Keys) - { - foreach (QueryDrainingMode queryDrainingMode2 in queryExecutionResults.Keys) - { - if (queryDrainingMode1 != queryDrainingMode2) - { - List first = queryExecutionResults[queryDrainingMode1]; - List second = queryExecutionResults[queryDrainingMode2]; - Assert.IsTrue(first.SequenceEqual(second)); - } - } - } - - return queryExecutionResults.Values.First(); - } - - internal static async IAsyncEnumerable> RunSimpleQueryAsync( - Container container, - string query, - QueryRequestOptions requestOptions = null) - { - using (FeedIterator resultSetIterator = container.GetItemQueryIterator( - query, - requestOptions: requestOptions)) - { - while (resultSetIterator.HasMoreResults) - { - FeedResponse response = await resultSetIterator.ReadNextAsync(); - yield return response; - } - } - } - - internal static async IAsyncEnumerable> RunSimpleQueryWithNewIteratorAsync( - Container container, - string query, - QueryRequestOptions requestOptions = null) - { - string continuationToken = null; - while (true) - { - using (FeedIterator resultSetIterator = container.GetItemQueryIterator( - query, - continuationToken, - requestOptions: requestOptions)) - { - while (resultSetIterator.HasMoreResults) - { - FeedResponse response = await resultSetIterator.ReadNextAsync(); - - continuationToken = response.ContinuationToken; - - yield return response; - - break; - } - - if (!resultSetIterator.HasMoreResults) - break; - } - } - } - - internal static async IAsyncEnumerable RunSimpleQueryAsync( - Container container, - string query, - QueryRequestOptions requestOptions = null) - { - using FeedIterator resultSetIterator = container.GetItemQueryStreamIterator( - query, - null, - requestOptions: requestOptions); - - while (resultSetIterator.HasMoreResults) - { - ResponseMessage response = await resultSetIterator.ReadNextAsync(); - yield return response; - } - } - - internal async Task> RunSinglePartitionQuery( - Container container, - string query, - QueryRequestOptions requestOptions = null) - { - List items = new List(); - using (FeedIterator resultSetIterator = container.GetItemQueryIterator( - query, - requestOptions: requestOptions)) - { - while (resultSetIterator.HasMoreResults) - { - items.AddRange(await resultSetIterator.ReadNextAsync()); - } - } - - return items; - } - - private class LocalCounter - { - private long value; - - public long Value => this.value; - - public long IncrementBy(long incrementBy) - { - return Interlocked.Add(ref this.value, incrementBy); - } - } - - internal class RequestChargeTrackingHandler : RequestHandler - { - private double totalRequestCharge; - private bool isEnabled; - - public void StartTracking() - { - this.isEnabled = true; - } - - public double StopTracking() - { - double requestCharge = this.totalRequestCharge; - this.isEnabled = false; - this.totalRequestCharge = 0; - return requestCharge; - } - - public override async Task SendAsync(RequestMessage request, CancellationToken cancellationToken) - { - ResponseMessage response = await base.SendAsync(request, cancellationToken); - - if (this.isEnabled) - { - this.AddRequestCharge(response.Headers.RequestCharge); - } - - return response; - } - - private void AddRequestCharge(double requestCharge) - { - if (requestCharge == 0) - { - return; - } - - double startValue; - double currentValue = this.totalRequestCharge; - - do - { - startValue = currentValue; - double targetValue = currentValue + requestCharge; - currentValue = Interlocked.CompareExchange(ref this.totalRequestCharge, targetValue, startValue); - } while (currentValue != startValue); - } - } - } + vectorEmbeddingPolicy), + _ => throw new ArgumentException($"Unknown {nameof(CollectionTypes)} : {collectionType}"), + }; + collectionsAndDocuments.Add(await createContainerTask); + } + + List cosmosClients = new List(); + foreach (ConnectionModes connectionMode in Enum.GetValues(connectionModes.GetType()).Cast().Where(connectionModes.HasFlag)) + { + if (connectionMode == ConnectionModes.None) + { + continue; + } + + ConnectionMode targetConnectionMode = GetTargetConnectionMode(connectionMode); + CosmosClient cosmosClient = cosmosClientFactory(targetConnectionMode); + + Assert.AreEqual( + targetConnectionMode, + cosmosClient.ClientOptions.ConnectionMode, + "Test setup: Invalid connection policy applied to CosmosClient"); + cosmosClients.Add(cosmosClient); + } + + List queryTasks = new List(); + foreach (CosmosClient cosmosClient in cosmosClients) + { + foreach ((Container container, IReadOnlyList insertedDocuments) in collectionsAndDocuments) + { + Task queryTask = Task.Run(() => query(container, insertedDocuments, testArgs)); + queryTasks.Add(queryTask); + } + } + + await Task.WhenAll(queryTasks); + + List> deleteContainerTasks = new List>(); + foreach (Container container in collectionsAndDocuments.Select(tuple => tuple.Item1)) + { + deleteContainerTasks.Add(container.DeleteContainerAsync()); + } + + await Task.WhenAll(deleteContainerTasks); + } + catch (Exception ex) when (ex.GetType() != typeof(AssertFailedException)) + { + while (ex.InnerException != null) + { + ex = ex.InnerException; + } + + ExceptionDispatchInfo.Capture(ex).Throw(); + } + } + + private static ConnectionMode GetTargetConnectionMode(ConnectionModes connectionMode) + { + return connectionMode switch + { + ConnectionModes.Gateway => ConnectionMode.Gateway, + ConnectionModes.Direct => ConnectionMode.Direct, + _ => throw new ArgumentException($"Unexpected connection mode: {connectionMode}"), + }; + } + + internal CosmosClient CreateDefaultCosmosClient(ConnectionMode connectionMode) + { + return connectionMode switch + { + ConnectionMode.Gateway => this.GatewayClient, + ConnectionMode.Direct => this.Client, + _ => throw new ArgumentException($"Unexpected connection mode: {connectionMode}"), + }; + } + + internal CosmosClient CreateNewCosmosClient(ConnectionMode connectionMode) + { + return connectionMode switch + { + ConnectionMode.Gateway => TestCommon.CreateCosmosClient(true), + ConnectionMode.Direct => TestCommon.CreateCosmosClient(false), + _ => throw new ArgumentException($"Unexpected connection mode: {connectionMode}"), + }; + } + + internal static async Task> QueryWithContinuationTokensAsync( + Container container, + string query, + QueryRequestOptions queryRequestOptions = null) + { + if (queryRequestOptions == null) + { + queryRequestOptions = new QueryRequestOptions(); + } + + List resultsFromContinuationToken = new List(); + int resultCount = 0; + string continuationToken = null; + do + { + FeedIterator itemQuery = container.GetItemQueryIterator( + queryText: query, + requestOptions: queryRequestOptions, + continuationToken: continuationToken); + + try + { + while (true) + { + try + { + FeedResponse cosmosQueryResponse = await itemQuery.ReadNextAsync(); + if (queryRequestOptions.MaxItemCount.HasValue) + { + Assert.IsTrue( + cosmosQueryResponse.Count <= queryRequestOptions.MaxItemCount.Value, + $"Max Item Count is not being honored. Got {cosmosQueryResponse.Count} when {queryRequestOptions.MaxItemCount.Value} is the max."); + } + + resultsFromContinuationToken.AddRange(cosmosQueryResponse); + resultCount += cosmosQueryResponse.Count; + continuationToken = cosmosQueryResponse.ContinuationToken; + break; + } + catch (CosmosException cosmosException) when (cosmosException.StatusCode == (HttpStatusCode)429) + { + itemQuery.Dispose(); + itemQuery = container.GetItemQueryIterator( + queryText: query, + requestOptions: queryRequestOptions, + continuationToken: continuationToken); + } + } + } + finally + { + itemQuery.Dispose(); + } + } while (continuationToken != null); + + Assert.AreEqual(resultsFromContinuationToken.Count, resultCount); + return resultsFromContinuationToken; + } + + internal static async Task> QueryWithoutContinuationTokensAsync( + Container container, + string query, + QueryRequestOptions queryRequestOptions = null) + { + if (queryRequestOptions == null) + { + queryRequestOptions = new QueryRequestOptions(); + } + + int resultCount = 0; + List results = new List(); + FeedIterator itemQuery = container.GetItemQueryIterator( + queryText: query, + requestOptions: queryRequestOptions); + try + { + string continuationTokenForRetries = null; + while (itemQuery.HasMoreResults) + { + try + { + FeedResponse page = await itemQuery.ReadNextAsync(); + results.AddRange(page); + resultCount += page.Count; + + if (queryRequestOptions.MaxItemCount.HasValue) + { + if (page.Count > queryRequestOptions.MaxItemCount.Value) + { + Console.WriteLine(); + } + Assert.IsTrue( + page.Count <= queryRequestOptions.MaxItemCount.Value, + $"Max Item Count is not being honored. Got {page.Count} documents when the max is {queryRequestOptions.MaxItemCount.Value}."); + } + + try + { + continuationTokenForRetries = page.ContinuationToken; + } + catch (Exception) + { + // Grabbing a continuation token is not supported on all queries. + } + } + catch (CosmosException cosmosException) when (cosmosException.StatusCode == (HttpStatusCode)429) + { + itemQuery.Dispose(); + itemQuery = container.GetItemQueryIterator( + queryText: query, + requestOptions: queryRequestOptions, + continuationToken: continuationTokenForRetries); + + if (continuationTokenForRetries == null) + { + // The query failed and we don't have a save point, so just restart the whole thing. + results = new List(); + resultCount = 0; + } + } + } + } + finally + { + itemQuery.Dispose(); + } + + Assert.AreEqual(results.Count, resultCount); + return results; + } + + internal static async Task NoOp() + { + await Task.Delay(0); + } + + internal static Task> RunQueryAsync( + Container container, + string query, + QueryRequestOptions queryRequestOptions = null) + { + return RunQueryAsync(container, query, queryRequestOptions); + } + + internal static Task> RunQueryAsync( + Container container, + string query, + QueryRequestOptions queryRequestOptions = null) + { + return RunQueryCombinationsAsync( + container, + query, + queryRequestOptions, + QueryDrainingMode.ContinuationToken | QueryDrainingMode.HoldState); + } + + [Flags] + public enum QueryDrainingMode + { + None = 0, + HoldState = 1, + ContinuationToken = 2, + } + + internal static Task> RunQueryCombinationsAsync( + Container container, + string query, + QueryRequestOptions queryRequestOptions, + QueryDrainingMode queryDrainingMode) + { + return RunQueryCombinationsAsync(container, query, queryRequestOptions, queryDrainingMode); + } + + internal static async Task> RunQueryCombinationsAsync( + Container container, + string query, + QueryRequestOptions queryRequestOptions, + QueryDrainingMode queryDrainingMode) + { + if (queryDrainingMode == QueryDrainingMode.None) + { + throw new ArgumentOutOfRangeException(nameof(queryDrainingMode)); + } + + Dictionary> queryExecutionResults = new Dictionary>(); + + if (queryDrainingMode.HasFlag(QueryDrainingMode.HoldState)) + { + List queryResultsWithoutContinuationToken = await QueryWithoutContinuationTokensAsync( + container, + query, + queryRequestOptions); + + queryExecutionResults[QueryDrainingMode.HoldState] = queryResultsWithoutContinuationToken; + } + + if (queryDrainingMode.HasFlag(QueryDrainingMode.ContinuationToken)) + { + List queryResultsWithContinuationTokens = await QueryWithContinuationTokensAsync( + container, + query, + queryRequestOptions); + + queryExecutionResults[QueryDrainingMode.ContinuationToken] = queryResultsWithContinuationTokens; + } + + foreach (QueryDrainingMode queryDrainingMode1 in queryExecutionResults.Keys) + { + foreach (QueryDrainingMode queryDrainingMode2 in queryExecutionResults.Keys) + { + if (queryDrainingMode1 != queryDrainingMode2) + { + List first = queryExecutionResults[queryDrainingMode1]; + List second = queryExecutionResults[queryDrainingMode2]; + Assert.IsTrue(first.SequenceEqual(second)); + } + } + } + + return queryExecutionResults.Values.First(); + } + + internal static async IAsyncEnumerable> RunSimpleQueryAsync( + Container container, + string query, + QueryRequestOptions requestOptions = null) + { + using (FeedIterator resultSetIterator = container.GetItemQueryIterator( + query, + requestOptions: requestOptions)) + { + while (resultSetIterator.HasMoreResults) + { + FeedResponse response = await resultSetIterator.ReadNextAsync(); + yield return response; + } + } + } + + internal static async IAsyncEnumerable> RunSimpleQueryWithNewIteratorAsync( + Container container, + string query, + QueryRequestOptions requestOptions = null) + { + string continuationToken = null; + while (true) + { + using (FeedIterator resultSetIterator = container.GetItemQueryIterator( + query, + continuationToken, + requestOptions: requestOptions)) + { + while (resultSetIterator.HasMoreResults) + { + FeedResponse response = await resultSetIterator.ReadNextAsync(); + + continuationToken = response.ContinuationToken; + + yield return response; + + break; + } + + if (!resultSetIterator.HasMoreResults) + break; + } + } + } + + internal static async IAsyncEnumerable RunSimpleQueryAsync( + Container container, + string query, + QueryRequestOptions requestOptions = null) + { + using FeedIterator resultSetIterator = container.GetItemQueryStreamIterator( + query, + null, + requestOptions: requestOptions); + + while (resultSetIterator.HasMoreResults) + { + ResponseMessage response = await resultSetIterator.ReadNextAsync(); + yield return response; + } + } + + internal async Task> RunSinglePartitionQuery( + Container container, + string query, + QueryRequestOptions requestOptions = null) + { + List items = new List(); + using (FeedIterator resultSetIterator = container.GetItemQueryIterator( + query, + requestOptions: requestOptions)) + { + while (resultSetIterator.HasMoreResults) + { + items.AddRange(await resultSetIterator.ReadNextAsync()); + } + } + + return items; + } + + private class LocalCounter + { + private long value; + + public long Value => this.value; + + public long IncrementBy(long incrementBy) + { + return Interlocked.Add(ref this.value, incrementBy); + } + } + + internal class RequestChargeTrackingHandler : RequestHandler + { + private double totalRequestCharge; + private bool isEnabled; + + public void StartTracking() + { + this.isEnabled = true; + } + + public double StopTracking() + { + double requestCharge = this.totalRequestCharge; + this.isEnabled = false; + this.totalRequestCharge = 0; + return requestCharge; + } + + public override async Task SendAsync(RequestMessage request, CancellationToken cancellationToken) + { + ResponseMessage response = await base.SendAsync(request, cancellationToken); + + if (this.isEnabled) + { + this.AddRequestCharge(response.Headers.RequestCharge); + } + + return response; + } + + private void AddRequestCharge(double requestCharge) + { + if (requestCharge == 0) + { + return; + } + + double startValue; + double currentValue = this.totalRequestCharge; + + do + { + startValue = currentValue; + double targetValue = currentValue + requestCharge; + currentValue = Interlocked.CompareExchange(ref this.totalRequestCharge, targetValue, startValue); + } while (currentValue != startValue); + } + } + } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/SemanticRerankingIntegrationTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/SemanticRerankingIntegrationTests.cs new file mode 100644 index 0000000000..40f4a6ae39 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/SemanticRerankingIntegrationTests.cs @@ -0,0 +1,118 @@ +namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests +{ + using System; + using System.Collections.Generic; + using System.Text.Json; + using System.Text.Json.Serialization; + using System.Threading; + using System.Threading.Tasks; + using global::Azure.Core; + using global::Azure.Identity; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using static Microsoft.Azure.Cosmos.SDK.EmulatorTests.MultiRegionSetupHelpers; + + [TestClass] + public class SemanticRerankingIntegrationTests + { + private string connectionString; + private CosmosClient client; + + private CosmosSystemTextJsonSerializer cosmosSystemTextJsonSerializer; + + [TestInitialize] + public void TestInitAsync() + { + this.connectionString = "https://inferencee2etest.documents.azure.com:443/"; + Environment.SetEnvironmentVariable("AZURE_COSMOS_SEMANTIC_RERANKER_INFERENCE_ENDPOINT", "https://inferencee2etest.dbinference.azure.com"); + DefaultAzureCredentialOptions options = new DefaultAzureCredentialOptions + { + TenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47", + ExcludeVisualStudioCredential = true + }; + + //Create a cosmos client using AAD authentication + TokenCredential tokenCredential = new DefaultAzureCredential(options); + + JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + this.cosmosSystemTextJsonSerializer = new MultiRegionSetupHelpers.CosmosSystemTextJsonSerializer(jsonSerializerOptions); + + if (string.IsNullOrEmpty(this.connectionString)) + { + Assert.Fail("Set environment variable COSMOSDB_MULTI_REGION to run the tests"); + } + this.client = new CosmosClient( + this.connectionString, + tokenCredential, + new CosmosClientOptions() + { + Serializer = this.cosmosSystemTextJsonSerializer, + }); + } + + [TestCleanup] + public void TestCleanup() + { + this.client?.Dispose(); + } + +#if PREVIEW + [TestMethod] + [TestCategory("Ignore")] + [Timeout(70000)] + public async Task SemanticRerankTest() + { + Database db = this.client.GetDatabase("virtualstore"); + Container container = db.GetContainer("sportinggoods"); + + string search_text = "integrated pull-up bar"; + + string queryString = $@" + SELECT TOP 15 c.id, c.Name, c.Brand, c.Description + FROM c + WHERE FullTextContains(c.Description, ""{search_text}"") + ORDER BY RANK FullTextScore(c.Description, ""{search_text}"") + "; + + string reranking_context = "most economical with multiple pulley adjustmnets and ideal for home gyms"; + + List documents = new List(); + FeedIterator resultSetIterator = container.GetItemQueryIterator( + new QueryDefinition(queryString), + requestOptions: new QueryRequestOptions() + { + MaxItemCount = 15, + }); + + while (resultSetIterator.HasMoreResults) + { + FeedResponse response = await resultSetIterator.ReadNextAsync(); + foreach (JsonElement item in response) + { + documents.Add(item.ToString()); + } + } + + Dictionary options = new Dictionary + { + { "return_documents", true }, + { "top_k", 10 }, + { "batch_size", 32 }, + { "sort", true } + }; + + SemanticRerankResult results = await container.SemanticRerankAsync( + reranking_context, + documents, + options); + + Assert.IsTrue(results.RerankScores.Count > 0); + Assert.AreEqual(4, results.RerankScores[0].Index); + Assert.IsNotNull(results.Latency); + Assert.IsNotNull(results.TokenUseage); + } +#endif + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/EndToEndTraceWriterBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/EndToEndTraceWriterBaselineTests.cs index d056374f38..9e8e2250c1 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/EndToEndTraceWriterBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/EndToEndTraceWriterBaselineTests.cs @@ -46,10 +46,6 @@ public sealed class EndToEndTraceWriterBaselineTests : BaselineTests m.GetCustomAttributes(typeof(TestMethodAttribute), false).Length > 0).Count(); - - private static int MethodCount = 0; - [ClassInitialize] public static async Task ClassInitAsync(TestContext _) { @@ -127,33 +123,34 @@ public static async Task ClassInitAsync(TestContext _) EndToEndTraceWriterBaselineTests.AssertAndResetActivityInformation(); } - [TestCleanup] - public async Task CleanUp() + [ClassCleanup] + public static async Task CleanUp() { await EndToEndTraceWriterBaselineTests.ClassCleanupAsync(); } + + [TestCleanup] + public void TestCleanup() + { + EndToEndTraceWriterBaselineTests.AssertAndResetActivityInformation(); + } public static async Task ClassCleanupAsync() { - EndToEndTraceWriterBaselineTests.MethodCount++; - - if (EndToEndTraceWriterBaselineTests.MethodCount == EndToEndTraceWriterBaselineTests.TotalTestMethod) + if (database != null) { - if (database != null) - { - await EndToEndTraceWriterBaselineTests.database.DeleteStreamAsync(); - } - - EndToEndTraceWriterBaselineTests.client?.Dispose(); - EndToEndTraceWriterBaselineTests.bulkClient?.Dispose(); - EndToEndTraceWriterBaselineTests.miscCosmosClient?.Dispose(); + await EndToEndTraceWriterBaselineTests.database.DeleteStreamAsync(); + } + + EndToEndTraceWriterBaselineTests.client?.Dispose(); + EndToEndTraceWriterBaselineTests.bulkClient?.Dispose(); + EndToEndTraceWriterBaselineTests.miscCosmosClient?.Dispose(); - Util.DisposeOpenTelemetryAndCustomListeners(); + Util.DisposeOpenTelemetryAndCustomListeners(); - EndToEndTraceWriterBaselineTests.testListener.Dispose(); + EndToEndTraceWriterBaselineTests.testListener.Dispose(); - Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", null); - } + Environment.SetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN", null); } private static void AssertAndResetActivityInformation() @@ -1865,6 +1862,8 @@ public TraceForBaselineTesting( public IReadOnlyDictionary Data => this.data; + public bool IsBeingWalked => true; // needs to return true to allow materialization + public IReadOnlyList<(string, Uri)> RegionsContacted => new List<(string, Uri)>(); public void AddDatum(string key, TraceDatum traceDatum) @@ -1924,6 +1923,11 @@ public void AddOrUpdateDatum(string key, object value) this.data[key] = "Redacted To Not Change The Baselines From Run To Run"; } + + bool ITrace.TryGetDatum(string key, out object datum) + { + return this.data.TryGetValue(key, out datum); + } } private sealed class RequestHandlerSleepHelper : RequestHandler diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/TraceJoiner.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/TraceJoiner.cs index ab8ef13a8d..369c146f61 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/TraceJoiner.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/TraceJoiner.cs @@ -65,6 +65,8 @@ public TraceForest(IReadOnlyList children) public IReadOnlyDictionary Data => this.data; + public bool IsBeingWalked => true; // needs to return true to allow materialization + public IReadOnlyList<(string, Uri)> RegionsContacted => new List<(string, Uri)>(); public void AddDatum(string key, TraceDatum traceDatum) @@ -107,6 +109,11 @@ public void AddOrUpdateDatum(string key, object value) { this.data[key] = value; } + + bool ITrace.TryGetDatum(string key, out object datum) + { + return this.data.TryGetValue(key, out datum); + } } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/LocalEmulatorTokenCredential.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/LocalEmulatorTokenCredential.cs index c40a5e331f..0d8da95a0b 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/LocalEmulatorTokenCredential.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/LocalEmulatorTokenCredential.cs @@ -18,7 +18,7 @@ public class LocalEmulatorTokenCredential : TokenCredential private readonly DateTime? DefaultDateTime = null; private readonly Action GetTokenCallback; private readonly string masterKey; - private readonly string expectedScope; + private readonly string[] expectedScopes; internal LocalEmulatorTokenCredential( string expectedScope, @@ -29,7 +29,19 @@ internal LocalEmulatorTokenCredential( this.masterKey = masterKey; this.GetTokenCallback = getTokenCallback; this.DefaultDateTime = defaultDateTime; - this.expectedScope = expectedScope; + this.expectedScopes = new string[] { expectedScope }; + } + + internal LocalEmulatorTokenCredential( + string[] expectedScopes, + string masterKey = null, + Action getTokenCallback = null, + DateTime? defaultDateTime = null) + { + this.masterKey = masterKey; + this.GetTokenCallback = getTokenCallback; + this.DefaultDateTime = defaultDateTime; + this.expectedScopes = expectedScopes; } public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken) @@ -44,7 +56,7 @@ public override ValueTask GetTokenAsync(TokenRequestContext request private AccessToken GetAccessToken(TokenRequestContext requestContext, CancellationToken cancellationToken) { - Assert.AreEqual(this.expectedScope, requestContext.Scopes.First()); + Assert.IsTrue(this.expectedScopes.Contains(requestContext.Scopes.First())); this.GetTokenCallback?.Invoke( requestContext, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs index 74b12a9e04..26513b7160 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs @@ -180,9 +180,9 @@ private void Init() { Tuple.Create(new PartitionKeyRange{ Id = "0", MinInclusive = "", MaxExclusive = "FF"}, (ServiceIdentity)null) }, - string.Empty); + string.Empty, false); - this.partitionKeyRangeCache = new Mock(null, null, null, null, false); + this.partitionKeyRangeCache = new Mock(null, null, null, null, false, false); this.partitionKeyRangeCache.Setup( m => m.TryLookupAsync( It.IsAny(), diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ActiveClientDiagnosticTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ActiveClientDiagnosticTest.cs index 898751c818..5738c44371 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ActiveClientDiagnosticTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ActiveClientDiagnosticTest.cs @@ -12,7 +12,7 @@ namespace Microsoft.Azure.Cosmos.Tests [TestClass] public class ActiveClientDiagnosticTest { - private const string ConnectionString = "AccountEndpoint=https://localtestcosmos.documents.azure.com:443/;AccountKey=425Mcv8CXQqzRNCgFNjIhT424GK99CKJvASowTnq15Vt8LeahXTcN5wt3342vQ==;"; + private const string ConnectionString = "AccountEndpoint=https://example.documents.azure.com:443/;AccountKey=NotRealKey==;"; [TestInitialize] public void Initialize() diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Authorization/CosmosScopeProviderTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Authorization/CosmosScopeProviderTests.cs new file mode 100644 index 0000000000..b4c99fbf8b --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Authorization/CosmosScopeProviderTests.cs @@ -0,0 +1,78 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos.Tests.Authorization +{ + using System; + using global::Azure.Core; + using Microsoft.Azure.Cosmos.Authorization; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class CosmosScopeProviderTests + { + private static readonly Uri TestAccountEndpoint = new Uri("https://testaccount.documents.azure.com:443/"); + + [DataTestMethod] + [DataRow("https://override/.default", "https://override/.default", DisplayName = "OverrideScope_Used")] + [DataRow(null, "https://testaccount.documents.azure.com/.default", DisplayName = "AccountScope_Used_WhenNoOverride")] + public void GetTokenRequestContext_UsesExpectedScope(string overrideScope, string expectedScope) + { + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", overrideScope); + + try + { + CosmosScopeProvider provider = new CosmosScopeProvider(TestAccountEndpoint); + TokenRequestContext context = provider.GetTokenRequestContext(); + Assert.AreEqual(expectedScope, context.Scopes[0]); + } + finally + { + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", null); + } + } + + [DataTestMethod] + [DataRow("https://override/.default", false, "AADSTS500011", "https://override/.default", DisplayName = "OverrideScope_NeverFallback")] + [DataRow(null, true, "AADSTS500011", "https://cosmos.azure.com/.default", DisplayName = "AccountScope_FallbacksToAadDefault")] + [DataRow(null, false, "SomeOtherError", "https://testaccount.documents.azure.com/.default", DisplayName = "AccountScope_NoFallbackOnOtherError")] + public void Test_TryFallback_Behavior( + string overrideScope, + bool expectFallback, + string exceptionMessage, + string expectedScope) + { + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", overrideScope); + + try + { + CosmosScopeProvider provider = new CosmosScopeProvider(TestAccountEndpoint); + + bool didFallback = provider.TryFallback(new Exception(exceptionMessage)); + + Assert.AreEqual(expectFallback, didFallback, "Fallback result mismatch."); + Assert.AreEqual(expectedScope, provider.GetTokenRequestContext().Scopes[0]); + } + finally + { + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", null); + } + } + + [TestMethod] + public void TryFallback_DoesNotFallback_WhenAlreadyUsingAadDefault() + { + Environment.SetEnvironmentVariable("AZURE_COSMOS_AAD_SCOPE_OVERRIDE", null); + CosmosScopeProvider provider = new CosmosScopeProvider(TestAccountEndpoint); + + provider.TryFallback(new Exception("AADSTS500011")); + Assert.AreEqual("https://cosmos.azure.com/.default", provider.GetTokenRequestContext().Scopes[0]); + + // Act + bool didFallbackAgain = provider.TryFallback(new Exception("AADSTS500011")); + + Assert.IsFalse(didFallbackAgain, "Should not fallback again when already using AadDefault scope."); + Assert.AreEqual("https://cosmos.azure.com/.default", provider.GetTokenRequestContext().Scopes[0]); + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.GroupBy.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.GroupBy.xml index d3d4fdae81..fa94108d23 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.GroupBy.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.GroupBy.xml @@ -543,22 +543,22 @@ System.Runtime.InteropServices.COMException : 0x800A0B00 diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.OffsetLimit.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.OffsetLimit.xml index 7521a89657..4f54d9e595 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.OffsetLimit.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.OffsetLimit.xml @@ -454,7 +454,7 @@ System.Runtime.InteropServices.COMException : 0x800A0B00 OFFSET value beyond lower range - hybrid search - SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET -1 LIMIT 10 + SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET -1 LIMIT 10 /key @@ -462,7 +462,7 @@ System.Runtime.InteropServices.COMException : 0x800A0B00 Microsoft.Azure.Cosmos.Query.Core.Monads.ExceptionWithStackTraceException : TryCatch resulted in an exception. -Microsoft.Azure.Cosmos.Query.Core.Exceptions.ExpectedQueryPartitionProviderException : {"errors":[{"severity":"Error","location":{"start":74,"end":75},"code":"SC1001","message":"Syntax error, incorrect syntax near '-'."}]} +Microsoft.Azure.Cosmos.Query.Core.Exceptions.ExpectedQueryPartitionProviderException : {"errors":[{"severity":"Error","location":{"start":72,"end":73},"code":"SC1001","message":"Syntax error, incorrect syntax near '-'."}]} System.Runtime.InteropServices.COMException : 0x800A0B00 @@ -470,7 +470,7 @@ System.Runtime.InteropServices.COMException : 0x800A0B00 OFFSET value at lower range - hybrid search - SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 0 LIMIT 10 + SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 0 LIMIT 10 /key @@ -518,7 +518,7 @@ OFFSET 0 LIMIT 120]]> OFFSET value at upper range (client) - hybrid search - SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 2147483647 LIMIT 10 + SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 2147483647 LIMIT 10 /key @@ -566,7 +566,7 @@ OFFSET 0 LIMIT 18]]> OFFSET value beyond upper range (client) - hybrid search - SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 2147483648 LIMIT 10 + SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 2147483648 LIMIT 10 /key @@ -582,7 +582,7 @@ System.ArgumentOutOfRangeException : Specified argument was out of the range of OFFSET value beyond upper range (Interop) - hybrid search - SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 4294967296 LIMIT 10 + SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 4294967296 LIMIT 10 /key @@ -590,7 +590,7 @@ System.ArgumentOutOfRangeException : Specified argument was out of the range of Microsoft.Azure.Cosmos.Query.Core.Monads.ExceptionWithStackTraceException : TryCatch resulted in an exception. -Microsoft.Azure.Cosmos.Query.Core.Exceptions.ExpectedQueryPartitionProviderException : {"errors":[{"severity":"Error","location":{"start":74,"end":84},"code":"SC2062","message":"The OFFSET count value exceeds the maximum allowed value."}]} +Microsoft.Azure.Cosmos.Query.Core.Exceptions.ExpectedQueryPartitionProviderException : {"errors":[{"severity":"Error","location":{"start":72,"end":82},"code":"SC2062","message":"The OFFSET count value exceeds the maximum allowed value."}]} System.Runtime.InteropServices.COMException : 0x800A0B00 @@ -716,7 +716,7 @@ System.Runtime.InteropServices.COMException : 0x800A0B00 LIMIT value beyond lower range - hybrid search - SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 10 LIMIT -1 + SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 10 LIMIT -1 /key @@ -724,7 +724,7 @@ System.Runtime.InteropServices.COMException : 0x800A0B00 Microsoft.Azure.Cosmos.Query.Core.Monads.ExceptionWithStackTraceException : TryCatch resulted in an exception. -Microsoft.Azure.Cosmos.Query.Core.Exceptions.ExpectedQueryPartitionProviderException : {"errors":[{"severity":"Error","location":{"start":83,"end":84},"code":"SC1001","message":"Syntax error, incorrect syntax near '-'."}]} +Microsoft.Azure.Cosmos.Query.Core.Exceptions.ExpectedQueryPartitionProviderException : {"errors":[{"severity":"Error","location":{"start":81,"end":82},"code":"SC1001","message":"Syntax error, incorrect syntax near '-'."}]} System.Runtime.InteropServices.COMException : 0x800A0B00 @@ -732,7 +732,7 @@ System.Runtime.InteropServices.COMException : 0x800A0B00 LIMIT value at lower range - hybrid search - SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 10 LIMIT 0 + SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 10 LIMIT 0 /key @@ -780,7 +780,7 @@ OFFSET 0 LIMIT 120]]> LIMIT value at upper range (client) - hybrid search - SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 10 LIMIT 2147483647 + SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 10 LIMIT 2147483647 /key @@ -828,7 +828,7 @@ OFFSET 0 LIMIT 18]]> LIMIT value beyond upper range (client) - hybrid search - SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 10 LIMIT 2147483648 + SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 10 LIMIT 2147483648 /key @@ -844,7 +844,7 @@ System.ArgumentOutOfRangeException : Specified argument was out of the range of LIMIT value beyond upper range (Interop) - hybrid search - SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 10 LIMIT 4294967296 + SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 10 LIMIT 4294967296 /key @@ -852,7 +852,7 @@ System.ArgumentOutOfRangeException : Specified argument was out of the range of Microsoft.Azure.Cosmos.Query.Core.Monads.ExceptionWithStackTraceException : TryCatch resulted in an exception. -Microsoft.Azure.Cosmos.Query.Core.Exceptions.ExpectedQueryPartitionProviderException : {"errors":[{"severity":"Error","location":{"start":83,"end":93},"code":"SC2061","message":"The LIMIT count value exceeds the maximum allowed value."}]} +Microsoft.Azure.Cosmos.Query.Core.Exceptions.ExpectedQueryPartitionProviderException : {"errors":[{"severity":"Error","location":{"start":81,"end":91},"code":"SC2061","message":"The LIMIT count value exceeds the maximum allowed value."}]} System.Runtime.InteropServices.COMException : 0x800A0B00 diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Top.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Top.xml index 22cab01da0..8f7539420b 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Top.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Top.xml @@ -460,7 +460,7 @@ System.Runtime.InteropServices.COMException : 0x800A0B00 TOP value beyond lower range - hybrid search - SELECT TOP -1 c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) + SELECT TOP -1 c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') /key @@ -476,7 +476,7 @@ System.Runtime.InteropServices.COMException : 0x800A0B00 TOP value at lower range - hybrid search - SELECT TOP 0 c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) + SELECT TOP 0 c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') /key @@ -522,7 +522,7 @@ ORDER BY _FullTextScore(c.text, ["swim"], {documentdb-formattablehybridsearchque TOP value at upper range (client) - hybrid search - SELECT TOP 2147483647 c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) + SELECT TOP 2147483647 c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') /key @@ -568,7 +568,7 @@ ORDER BY _FullTextScore(c.text, ["swim"], {documentdb-formattablehybridsearchque TOP value beyond upper range (client) - hybrid search - SELECT TOP 2147483648 c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) + SELECT TOP 2147483648 c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') /key @@ -584,7 +584,7 @@ System.ArgumentOutOfRangeException : Specified argument was out of the range of TOP value beyond upper range (Interop) - hybrid search - SELECT TOP 4294967296 c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) + SELECT TOP 4294967296 c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') /key diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/TraceWriterBaselineTests.TraceData.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/TraceWriterBaselineTests.TraceData.xml index 13be13cdef..965a3c0ed7 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/TraceWriterBaselineTests.TraceData.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/TraceWriterBaselineTests.TraceData.xml @@ -330,6 +330,7 @@ "LSN": 1337, "PartitionKeyRangeId": "42", "GlobalCommittedLSN": 1234, + "GlobalNRegionCommittedGLSN": -1, "ItemLSN": 15, "UsingLocalLSN": true, "QuorumAckedLSN": 23, @@ -527,6 +528,7 @@ "LSN": 0, "PartitionKeyRangeId": null, "GlobalCommittedLSN": 0, + "GlobalNRegionCommittedGLSN": 0, "ItemLSN": 0, "UsingLocalLSN": false, "QuorumAckedLSN": 0, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncBatcherTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncBatcherTests.cs index a250395267..b00048da4f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncBatcherTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncBatcherTests.cs @@ -41,7 +41,7 @@ private ItemBatchOperation CreateItemBatchOperation(bool withContext = false) } private readonly BatchAsyncBatcherExecuteDelegate Executor - = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, CancellationToken cancellationToken) => + = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, ItemRequestOptions options, CancellationToken cancellationToken) => { List results = new List(); ItemBatchOperation[] arrayOperations = new ItemBatchOperation[request.Operations.Count]; @@ -82,7 +82,7 @@ private readonly BatchAsyncBatcherExecuteDelegate Executor }; private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithSplit - = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, CancellationToken cancellationToken) => + = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, ItemRequestOptions options, CancellationToken cancellationToken) => { List results = new List(); ItemBatchOperation[] arrayOperations = new ItemBatchOperation[request.Operations.Count]; @@ -126,7 +126,7 @@ private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithSplit }; private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithCompletingSplit - = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, CancellationToken cancellationToken) => + = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, ItemRequestOptions options, CancellationToken cancellationToken) => { List results = new List(); ItemBatchOperation[] arrayOperations = new ItemBatchOperation[request.Operations.Count]; @@ -170,7 +170,7 @@ private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithCompletingSplit }; private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithCompletingPartitionMigration - = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, CancellationToken cancellationToken) => + = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, ItemRequestOptions options, CancellationToken cancellationToken) => { List results = new List(); ItemBatchOperation[] arrayOperations = new ItemBatchOperation[request.Operations.Count]; @@ -214,7 +214,7 @@ private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithCompletingPartitio }; private readonly BatchAsyncBatcherExecuteDelegate ExecutorWith413 - = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, CancellationToken cancellationToken) => + = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, ItemRequestOptions options, CancellationToken cancellationToken) => { List results = new List(); ItemBatchOperation[] arrayOperations = new ItemBatchOperation[request.Operations.Count]; @@ -269,7 +269,7 @@ private readonly BatchAsyncBatcherExecuteDelegate ExecutorWith413 }; private readonly BatchAsyncBatcherExecuteDelegate ExecutorWith413_3402 - = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, CancellationToken cancellationToken) => + = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, ItemRequestOptions options, CancellationToken cancellationToken) => { List results = new List(); ItemBatchOperation[] arrayOperations = new ItemBatchOperation[request.Operations.Count]; @@ -326,7 +326,7 @@ private readonly BatchAsyncBatcherExecuteDelegate ExecutorWith413_3402 // The response will include all but 2 operation responses private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithLessResponses - = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, CancellationToken cancellationToken) => + = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, ItemRequestOptions options, CancellationToken cancellationToken) => { int operationCount = request.Operations.Count - 2; List results = new List(); @@ -365,7 +365,7 @@ private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithLessResponses }; private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithFailure - = (PartitionKeyRangeServerBatchRequest request, ITrace trace, CancellationToken cancellationToken) => throw expectedException; + = (PartitionKeyRangeServerBatchRequest request, ITrace trace, ItemRequestOptions options, CancellationToken cancellationToken) => throw expectedException; private readonly BatchAsyncBatcherRetryDelegate Retrier = (ItemBatchOperation operation, CancellationToken cancellation) => Task.CompletedTask; @@ -799,7 +799,7 @@ private class ClientWithSplitDetection : MockDocumentClient public ClientWithSplitDetection() { - this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null, null, false); + this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null, null, false, false); this.partitionKeyRangeCache.Setup( m => m.TryGetOverlappingRangesAsync( It.IsAny(), diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncContainerExecutorTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncContainerExecutorTests.cs index d77afc627a..a814cd9834 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncContainerExecutorTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncContainerExecutorTests.cs @@ -62,7 +62,7 @@ public async Task RetryOnSplit() { Tuple.Create(new PartitionKeyRange{ Id = "0", MinInclusive = "", MaxExclusive = "FF"}, (ServiceIdentity)null) }, - string.Empty); + string.Empty, false); mockContainer.Setup(x => x.GetRoutingMapAsync(It.IsAny())).Returns(Task.FromResult(routingMap)); BatchAsyncContainerExecutor executor = new BatchAsyncContainerExecutor(mockContainer.Object, mockedContext.Object, 20, BatchAsyncContainerExecutorCache.DefaultMaxBulkRequestBodySizeInBytes); TransactionalBatchOperationResult result = await executor.AddAsync(itemBatchOperation, NoOpTrace.Singleton); @@ -120,7 +120,7 @@ public async Task RetryOnNameStale() { Tuple.Create(new PartitionKeyRange{ Id = "0", MinInclusive = "", MaxExclusive = "FF"}, (ServiceIdentity)null) }, - string.Empty); + string.Empty, false); mockContainer.Setup(x => x.GetRoutingMapAsync(It.IsAny())).Returns(Task.FromResult(routingMap)); BatchAsyncContainerExecutor executor = new BatchAsyncContainerExecutor(mockContainer.Object, mockedContext.Object, 20, BatchAsyncContainerExecutorCache.DefaultMaxBulkRequestBodySizeInBytes); TransactionalBatchOperationResult result = await executor.AddAsync(itemBatchOperation, NoOpTrace.Singleton); @@ -178,7 +178,7 @@ public async Task RetryOn429() { Tuple.Create(new PartitionKeyRange{ Id = "0", MinInclusive = "", MaxExclusive = "FF"}, (ServiceIdentity)null) }, - string.Empty); + string.Empty, false); mockContainer.Setup(x => x.GetRoutingMapAsync(It.IsAny())).Returns(Task.FromResult(routingMap)); BatchAsyncContainerExecutor executor = new BatchAsyncContainerExecutor(mockContainer.Object, mockedContext.Object, 20, BatchAsyncContainerExecutorCache.DefaultMaxBulkRequestBodySizeInBytes); TransactionalBatchOperationResult result = await executor.AddAsync(itemBatchOperation, NoOpTrace.Singleton); @@ -235,7 +235,7 @@ public async Task DoesNotRecalculatePartitionKeyRangeOnNoSplits() { Tuple.Create(new PartitionKeyRange{ Id = "0", MinInclusive = "", MaxExclusive = "FF"}, (ServiceIdentity)null) }, - string.Empty); + string.Empty, false); mockContainer.Setup(x => x.GetRoutingMapAsync(It.IsAny())).Returns(Task.FromResult(routingMap)); BatchAsyncContainerExecutor executor = new BatchAsyncContainerExecutor(mockContainer.Object, mockedContext.Object, 20, BatchAsyncContainerExecutorCache.DefaultMaxBulkRequestBodySizeInBytes); TransactionalBatchOperationResult result = await executor.AddAsync(itemBatchOperation, NoOpTrace.Singleton); @@ -377,7 +377,7 @@ private class ClientWithSplitDetection : MockDocumentClient public ClientWithSplitDetection() { - this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null, null, false); + this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null, null, false, false); this.partitionKeyRangeCache.Setup( m => m.TryGetOverlappingRangesAsync( It.IsAny(), diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncOperationContextTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncOperationContextTests.cs index b686449c42..e3d7348150 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncOperationContextTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncOperationContextTests.cs @@ -52,6 +52,11 @@ public async Task TraceIsJoinedOnCompletionWithRetry() batchAsyncOperationContext.Complete(null, result); + if (result.Trace is Trace rootLevelTrace) + { + rootLevelTrace.SetWalkingStateRecursively(); + } + Assert.AreEqual(result, await batchAsyncOperationContext.OperationTask); Assert.AreEqual(2, result.Trace.Children.Count, "The final trace should have the initial trace, plus the retries, plus the final trace"); Assert.AreEqual(rootTrace, result.Trace, "The first trace child should be the initial root"); @@ -93,6 +98,11 @@ public async Task TraceIsJoinedOnCompletionWithoutRetry() batchAsyncOperationContext.Complete(null, result); + if (result.Trace is Trace rootLevelTrace) + { + rootLevelTrace.SetWalkingStateRecursively(); + } + Assert.AreEqual(result, await batchAsyncOperationContext.OperationTask); Assert.AreEqual(1, result.Trace.Children.Count, "The final trace should have the initial trace, plus the final trace, since the result is not retried, it should not capture it"); Assert.AreEqual(rootTrace, result.Trace, "The first trace child should be the initial root"); @@ -287,7 +297,7 @@ private class ClientWithSplitDetection : MockDocumentClient public ClientWithSplitDetection() { - this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null, null, false); + this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null, null, false, false); this.partitionKeyRangeCache.Setup( m => m.TryGetOverlappingRangesAsync( It.IsAny(), diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncStreamerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncStreamerTests.cs index 606eb3d16f..2ee53dbdc2 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncStreamerTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncStreamerTests.cs @@ -28,7 +28,7 @@ public class BatchAsyncStreamerTests // Executor just returns a reponse matching the Id with Etag private readonly BatchAsyncBatcherExecuteDelegate Executor - = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, CancellationToken cancellationToken) => + = async (PartitionKeyRangeServerBatchRequest request, ITrace trace, ItemRequestOptions options, CancellationToken cancellationToken) => { List results = new List(); ItemBatchOperation[] arrayOperations = new ItemBatchOperation[request.Operations.Count]; @@ -67,7 +67,7 @@ private readonly BatchAsyncBatcherExecuteDelegate Executor return new PartitionKeyRangeBatchExecutionResult(request.PartitionKeyRangeId, request.Operations, batchresponse); }; - private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithFailure = (PartitionKeyRangeServerBatchRequest request, ITrace trace, CancellationToken cancellationToken) => throw expectedException; + private readonly BatchAsyncBatcherExecuteDelegate ExecutorWithFailure = (PartitionKeyRangeServerBatchRequest request, ITrace trace, ItemRequestOptions options, CancellationToken cancellationToken) => throw expectedException; private readonly BatchAsyncBatcherRetryDelegate Retrier = (ItemBatchOperation operation, CancellationToken cancellation) => Task.CompletedTask; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/PartitionKeyBatchResponseTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/PartitionKeyBatchResponseTests.cs index a396112dac..b7d832a8f9 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/PartitionKeyBatchResponseTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/PartitionKeyBatchResponseTests.cs @@ -125,6 +125,11 @@ public async Task DiagnosticsAreSetThroughResponseAsync() throw new Exception(); } + if (cosmosTraceDiagnostics.Value is Trace rootLevelTrace) + { + rootLevelTrace.SetWalkingStateRecursively(); + } + Assert.AreEqual(diagnostics, cosmosTraceDiagnostics.Value.Data.Values.First()); } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BulkPartitionKeyRangeGoneRetryPolicyTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BulkPartitionKeyRangeGoneRetryPolicyTests.cs index fe75375e6d..6c9cbd2e1b 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BulkPartitionKeyRangeGoneRetryPolicyTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BulkPartitionKeyRangeGoneRetryPolicyTests.cs @@ -145,7 +145,7 @@ private class ClientWithSplitDetection : MockDocumentClient public ClientWithSplitDetection() { - this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null, null, false); + this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null, null, false, false); this.partitionKeyRangeCache.Setup( m => m.TryGetOverlappingRangesAsync( It.IsAny(), diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CancellationTokenTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CancellationTokenTests.cs index 56640a2789..99c4ce7f8c 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CancellationTokenTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CancellationTokenTests.cs @@ -65,7 +65,8 @@ async Task sendFunc(HttpRequestMessage request) MockCosmosUtil.CreateCosmosHttpClient( () => new HttpClient(messageHandler), eventSource), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); @@ -221,7 +222,8 @@ private static GatewayStoreModel MockGatewayStoreModel(Func new HttpClient(messageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager.Object); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ChangeFeed/PartitionSynchronizerCoreTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ChangeFeed/PartitionSynchronizerCoreTests.cs index 140e8bb098..2e0dd999a6 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ChangeFeed/PartitionSynchronizerCoreTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ChangeFeed/PartitionSynchronizerCoreTests.cs @@ -63,6 +63,7 @@ public async Task HandlePartitionGoneAsync_PKRangeBasedLease_Split() Mock.Of(), new Mock(false).Object, this.endpointManager, + false, false); List resultingRanges = new List() @@ -128,6 +129,7 @@ public async Task HandlePartitionGoneAsync_EpkBasedLease_Split() Mock.Of(), new Mock(false).Object, this.endpointManager, + false, false); List resultingRanges = new List() @@ -198,6 +200,7 @@ public async Task HandlePartitionGoneAsync_PKRangeBasedLease_Merge() Mock.Of(), new Mock(false).Object, this.endpointManager, + false, false); List resultingRanges = new List() @@ -258,6 +261,7 @@ public async Task HandlePartitionGoneAsync_EpkBasedLease_Merge() Mock.Of(), new Mock(false).Object, this.endpointManager, + false, false); List resultingRanges = new List() @@ -308,6 +312,7 @@ public async Task CreateMissingLeases_NoLeases() Mock.Of(), new Mock(false).Object, this.endpointManager, + false, false); List resultingRanges = new List() @@ -355,6 +360,7 @@ public async Task CreateMissingLeases_SomePKRangeLeases() Mock.Of(), new Mock(false).Object, this.endpointManager, + false, false); List resultingRanges = new List() @@ -408,6 +414,7 @@ public async Task CreateMissingLeases_SomePKRangeAndEPKLeases() Mock.Of(), new Mock(false).Object, this.endpointManager, + false, false); List resultingRanges = new List() diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ClientRetryPolicyTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ClientRetryPolicyTests.cs index 691ddcccc3..56de565719 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ClientRetryPolicyTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ClientRetryPolicyTests.cs @@ -14,11 +14,11 @@ using System.Threading.Tasks; using Microsoft.Azure.Documents.Collections; using Microsoft.Azure.Documents.Client; - using Microsoft.Azure.Cosmos.Common; - using System.Net.Http; - using System.Reflection; - using System.Collections.Concurrent; - + using Microsoft.Azure.Cosmos.Common; + using System.Net.Http; + using System.Reflection; + using System.Collections.Concurrent; + /// /// Tests for /// @@ -28,6 +28,7 @@ public sealed class ClientRetryPolicyTests private static Uri Location1Endpoint = new Uri("https://location1.documents.azure.com"); private static Uri Location2Endpoint = new Uri("https://location2.documents.azure.com"); + private const string HubRegionHeader = "x-ms-cosmos-hub-region-processing-only"; private ReadOnlyCollection preferredLocations; private AccountProperties databaseAccount; private GlobalPartitionEndpointManager partitionKeyRangeLocationCache; @@ -50,7 +51,7 @@ public void MultimasterMetadataWriteRetryTest() multimasterMetadataWriteRetryTest: true); - ClientRetryPolicy retryPolicy = new ClientRetryPolicy(endpointManager, this.partitionKeyRangeLocationCache, new RetryOptions(), enableEndpointDiscovery, false, false); + ClientRetryPolicy retryPolicy = new ClientRetryPolicy(endpointManager, this.partitionKeyRangeLocationCache, new RetryOptions(), enableEndpointDiscovery, false); //Creates a metadata write request DocumentServiceRequest request = this.CreateRequest(false, true); @@ -86,38 +87,37 @@ public void MultimasterMetadataWriteRetryTest() retryPolicy.OnBeforeSendRequest(request); Assert.AreEqual(request.RequestContext.LocationEndpointToRoute, ClientRetryPolicyTests.Location1Endpoint); } - + /// - /// Test to validate that when 429.3092 is thrown from the service, write requests on + /// Test to validate that when 429.3092 is thrown from the service, write requests on /// a multi master account should be converted to 503 and retried to the next region. /// - [TestMethod] - [DataRow(true, DisplayName = "Validate retry policy with multi master write account.")] + [TestMethod] + [DataRow(true, DisplayName = "Validate retry policy with multi master write account.")] [DataRow(false, DisplayName = "Validate retry policy with single master write account.")] - public async Task ShouldRetryAsync_WhenRequestThrottledWithResourceNotAvailable_ShouldThrow503OnMultiMasterWriteAndRetryOnNextRegion( + public async Task ShouldRetryAsync_WhenRequestThrottledWithResourceNotAvailable_ShouldThrow503OnMultiMasterWriteAndRetryOnNextRegion( bool isMultiMasterAccount) - { - // Arrange. + { + // Arrange. const bool enableEndpointDiscovery = true; using GlobalEndpointManager endpointManager = this.Initialize( useMultipleWriteLocations: isMultiMasterAccount, enableEndpointDiscovery: enableEndpointDiscovery, isPreferredLocationsListEmpty: false, - multimasterMetadataWriteRetryTest: true); - + multimasterMetadataWriteRetryTest: true); + await endpointManager.RefreshLocationAsync(); - ClientRetryPolicy retryPolicy = new ( - endpointManager, - this.partitionKeyRangeLocationCache, - new RetryOptions(), - enableEndpointDiscovery, - false, + ClientRetryPolicy retryPolicy = new ( + endpointManager, + this.partitionKeyRangeLocationCache, + new RetryOptions(), + enableEndpointDiscovery, false); // Creates a sample write request. - DocumentServiceRequest request = this.CreateRequest( - isReadRequest: false, + DocumentServiceRequest request = this.CreateRequest( + isReadRequest: false, isMasterResourceType: false); // On first attempt should get (default/non hub) location. @@ -126,7 +126,7 @@ public async Task ShouldRetryAsync_WhenRequestThrottledWithResourceNotAvailable_ // Creation of 429.3092 Error. HttpStatusCode throttleException = HttpStatusCode.TooManyRequests; - SubStatusCodes resourceNotAvailable = SubStatusCodes.SystemResourceUnavailable; + SubStatusCodes resourceNotAvailable = SubStatusCodes.SystemResourceUnavailable; Exception innerException = new (); Mock nameValueCollection = new (); @@ -139,39 +139,39 @@ public async Task ShouldRetryAsync_WhenRequestThrottledWithResourceNotAvailable_ responseHeaders: nameValueCollection.Object); // Act. - Task shouldRetry = retryPolicy.ShouldRetryAsync( - documentClientException, - new CancellationToken()); - - // Assert. - Assert.IsTrue(shouldRetry.Result.ShouldRetry); - retryPolicy.OnBeforeSendRequest(request); - - if (isMultiMasterAccount) - { - Assert.AreEqual( - expected: ClientRetryPolicyTests.Location2Endpoint, - actual: request.RequestContext.LocationEndpointToRoute, - message: "The request should be routed to the next region, since the accound is a multi master write account and the request" + - "failed with 429.309 which got converted into 503 internally. This should trigger another retry attempt to the next region."); - } - else - { - Assert.AreEqual( - expected: ClientRetryPolicyTests.Location1Endpoint, - actual: request.RequestContext.LocationEndpointToRoute, - message: "Since this is asingle master account, the write request should not be retried on the next region."); - } - } + Task shouldRetry = retryPolicy.ShouldRetryAsync( + documentClientException, + new CancellationToken()); + + // Assert. + Assert.IsTrue(shouldRetry.Result.ShouldRetry); + retryPolicy.OnBeforeSendRequest(request); + + if (isMultiMasterAccount) + { + Assert.AreEqual( + expected: ClientRetryPolicyTests.Location2Endpoint, + actual: request.RequestContext.LocationEndpointToRoute, + message: "The request should be routed to the next region, since the accound is a multi master write account and the request" + + "failed with 429.309 which got converted into 503 internally. This should trigger another retry attempt to the next region."); + } + else + { + Assert.AreEqual( + expected: ClientRetryPolicyTests.Location1Endpoint, + actual: request.RequestContext.LocationEndpointToRoute, + message: "Since this is asingle master account, the write request should not be retried on the next region."); + } + } /// /// Tests to see if different 503 substatus and other similar status codes are handeled correctly /// /// The substatus code being Tested. [DataRow((int)StatusCodes.ServiceUnavailable, (int)SubStatusCodes.Unknown, "ServiceUnavailable")] - [DataRow((int)StatusCodes.ServiceUnavailable, (int)SubStatusCodes.TransportGenerated503, "ServiceUnavailable")] - [DataRow((int)StatusCodes.InternalServerError, (int)SubStatusCodes.Unknown, "InternalServerError")] - [DataRow((int)StatusCodes.Gone, (int)SubStatusCodes.LeaseNotFound, "LeaseNotFound")] + [DataRow((int)StatusCodes.ServiceUnavailable, (int)SubStatusCodes.TransportGenerated503, "ServiceUnavailable")] + [DataRow((int)StatusCodes.InternalServerError, (int)SubStatusCodes.Unknown, "InternalServerError")] + [DataRow((int)StatusCodes.Gone, (int)SubStatusCodes.LeaseNotFound, "LeaseNotFound")] [DataRow((int)StatusCodes.Forbidden, (int)SubStatusCodes.DatabaseAccountNotFound, "DatabaseAccountNotFound")] [DataTestMethod] public void Http503LikeSubStatusHandelingTests(int statusCode, int SubStatusCode, string message) @@ -185,8 +185,8 @@ public void Http503LikeSubStatusHandelingTests(int statusCode, int SubStatusCode isPreferredLocationsListEmpty: true); //Create Retry Policy - ClientRetryPolicy retryPolicy = new ClientRetryPolicy(endpointManager, this.partitionKeyRangeLocationCache, new RetryOptions(), enableEndpointDiscovery, false, false); - + ClientRetryPolicy retryPolicy = new ClientRetryPolicy(endpointManager, this.partitionKeyRangeLocationCache, new RetryOptions(), enableEndpointDiscovery, false); + CancellationToken cancellationToken = new CancellationToken(); Exception serviceUnavailableException = new Exception(); Mock nameValueCollection = new Mock(); @@ -205,155 +205,153 @@ public void Http503LikeSubStatusHandelingTests(int statusCode, int SubStatusCode Task retryStatus = retryPolicy.ShouldRetryAsync(documentClientException, cancellationToken); Assert.IsFalse(retryStatus.Result.ShouldRetry); - } - + } + /// - /// Tests to validate that when HttpRequestException is thrown while connecting to a gateway endpoint for a single master write account with PPAF enabled, + /// Tests to validate that when HttpRequestException is thrown while connecting to a gateway endpoint for a single master write account with PPAF enabled, /// a partition level failover is added and the request is retried to the next region. /// - [TestMethod] + [TestMethod] [DataRow(true, DisplayName = "Case when partition level failover is enabled.")] [DataRow(false, DisplayName = "Case when partition level failover is disabled.")] - public void HttpRequestExceptionHandelingTests( + public void HttpRequestExceptionHandelingTests( bool enablePartitionLevelFailover) - { - const bool enableEndpointDiscovery = true; - const string suffix = "-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF"; - - //Creates a sample write request - DocumentServiceRequest request = this.CreateRequest(false, false); + { + const bool enableEndpointDiscovery = true; + const string suffix = "-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF"; + + //Creates a sample write request + DocumentServiceRequest request = this.CreateRequest(false, false); request.RequestContext.ResolvedPartitionKeyRange = new PartitionKeyRange() { Id = "0" , MinInclusive = "3F" + suffix, MaxExclusive = "5F" + suffix }; //Create GlobalEndpointManager using GlobalEndpointManager endpointManager = this.Initialize( useMultipleWriteLocations: false, enableEndpointDiscovery: enableEndpointDiscovery, - isPreferredLocationsListEmpty: false, + isPreferredLocationsListEmpty: false, enablePartitionLevelFailover: enablePartitionLevelFailover); - - // Capture the read locations. - ReadOnlyCollection readLocations = endpointManager.ReadEndpoints; + + // Capture the read locations. + ReadOnlyCollection readLocations = endpointManager.ReadEndpoints; //Create Retry Policy - ClientRetryPolicy retryPolicy = new ( - globalEndpointManager: endpointManager, - partitionKeyRangeLocationCache: this.partitionKeyRangeLocationCache, - retryOptions: new RetryOptions(), - enableEndpointDiscovery: enableEndpointDiscovery, - isPartitionLevelFailoverEnabled: enablePartitionLevelFailover, - isThinClientEnabled: false); - + ClientRetryPolicy retryPolicy = new ( + globalEndpointManager: endpointManager, + partitionKeyRangeLocationCache: this.partitionKeyRangeLocationCache, + retryOptions: new RetryOptions(), + enableEndpointDiscovery: enableEndpointDiscovery, + isThinClientEnabled: false); + CancellationToken cancellationToken = new (); - HttpRequestException httpRequestException = new (message: "Connecting to endpoint has failed."); - - GlobalPartitionEndpointManagerCore.PartitionKeyRangeFailoverInfo partitionKeyRangeFailoverInfo = ClientRetryPolicyTests.GetPartitionKeyRangeFailoverInfoUsingReflection( - this.partitionKeyRangeLocationCache, - request.RequestContext.ResolvedPartitionKeyRange, - isReadOnlyOrMultiMasterWriteRequest: false); - - // Validate that the partition key range failover info is not present before the http request exception was captured in the retry policy. - Assert.IsNull(partitionKeyRangeFailoverInfo); - + HttpRequestException httpRequestException = new (message: "Connecting to endpoint has failed."); + + GlobalPartitionEndpointManagerCore.PartitionKeyRangeFailoverInfo partitionKeyRangeFailoverInfo = ClientRetryPolicyTests.GetPartitionKeyRangeFailoverInfoUsingReflection( + this.partitionKeyRangeLocationCache, + request.RequestContext.ResolvedPartitionKeyRange, + isReadOnlyOrMultiMasterWriteRequest: false); + + // Validate that the partition key range failover info is not present before the http request exception was captured in the retry policy. + Assert.IsNull(partitionKeyRangeFailoverInfo); + retryPolicy.OnBeforeSendRequest(request); - Task retryStatus = retryPolicy.ShouldRetryAsync(httpRequestException, cancellationToken); - - Assert.IsTrue(retryStatus.Result.ShouldRetry); - - partitionKeyRangeFailoverInfo = ClientRetryPolicyTests.GetPartitionKeyRangeFailoverInfoUsingReflection( - this.partitionKeyRangeLocationCache, - request.RequestContext.ResolvedPartitionKeyRange, - isReadOnlyOrMultiMasterWriteRequest: false); - - if (enablePartitionLevelFailover) - { - // Validate that the partition key range failover info to the next account region is present after the http request exception was captured in the retry policy. - Assert.AreEqual(partitionKeyRangeFailoverInfo.Current, readLocations[1]); - } - else - { - Assert.IsNull(partitionKeyRangeFailoverInfo); - } - } - + Task retryStatus = retryPolicy.ShouldRetryAsync(httpRequestException, cancellationToken); + + Assert.IsTrue(retryStatus.Result.ShouldRetry); + + partitionKeyRangeFailoverInfo = ClientRetryPolicyTests.GetPartitionKeyRangeFailoverInfoUsingReflection( + this.partitionKeyRangeLocationCache, + request.RequestContext.ResolvedPartitionKeyRange, + isReadOnlyOrMultiMasterWriteRequest: false); + + if (enablePartitionLevelFailover) + { + // Validate that the partition key range failover info to the next account region is present after the http request exception was captured in the retry policy. + Assert.AreEqual(partitionKeyRangeFailoverInfo.Current, readLocations[1]); + } + else + { + Assert.IsNull(partitionKeyRangeFailoverInfo); + } + } + /// - /// Test to validate that when an OperationCanceledException is thrown during the retry attempt, for a single master write account with PPAF enabled, + /// Test to validate that when an OperationCanceledException is thrown during the retry attempt, for a single master write account with PPAF enabled, /// a partition level failover is applied and the subsequent requests will be retried on the next region for the faulty partition. /// - [TestMethod] + [TestMethod] [DataRow(true, true, DisplayName = "Read Request - Case when partition level failover is enabled.")] [DataRow(false, true, DisplayName = "Write Request - Case when partition level failover is enabled.")] [DataRow(true, false, DisplayName = "Read Request - Case when partition level failover is disabled.")] [DataRow(false, false, DisplayName = "Write Request - Case when partition level failover is disabled.")] - public void CosmosOperationCancelledExceptionHandelingTests( - bool isReadOnlyRequest, + public void CosmosOperationCancelledExceptionHandelingTests( + bool isReadOnlyRequest, bool enablePartitionLevelFailover) - { - int requestThreshold = isReadOnlyRequest ? 10 : 5; - const bool enableEndpointDiscovery = true; - const string suffix = "-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF"; - - //Creates a sample write request - DocumentServiceRequest request = this.CreateRequest(isReadOnlyRequest, false); + { + int requestThreshold = isReadOnlyRequest ? 10 : 5; + const bool enableEndpointDiscovery = true; + const string suffix = "-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF"; + + //Creates a sample write request + DocumentServiceRequest request = this.CreateRequest(isReadOnlyRequest, false); request.RequestContext.ResolvedPartitionKeyRange = new PartitionKeyRange() { Id = "0", MinInclusive = "3F" + suffix, MaxExclusive = "5F" + suffix }; //Create GlobalEndpointManager using GlobalEndpointManager endpointManager = this.Initialize( useMultipleWriteLocations: false, enableEndpointDiscovery: enableEndpointDiscovery, - isPreferredLocationsListEmpty: false, + isPreferredLocationsListEmpty: false, enablePartitionLevelFailover: enablePartitionLevelFailover); - - // Capture the read locations. - ReadOnlyCollection readLocations = endpointManager.ReadEndpoints; + + // Capture the read locations. + ReadOnlyCollection readLocations = endpointManager.ReadEndpoints; //Create Retry Policy - ClientRetryPolicy retryPolicy = new( - globalEndpointManager: endpointManager, - partitionKeyRangeLocationCache: this.partitionKeyRangeLocationCache, - retryOptions: new RetryOptions(), - enableEndpointDiscovery: enableEndpointDiscovery, - isPartitionLevelFailoverEnabled: enablePartitionLevelFailover, - isThinClientEnabled: false); - + ClientRetryPolicy retryPolicy = new( + globalEndpointManager: endpointManager, + partitionKeyRangeLocationCache: this.partitionKeyRangeLocationCache, + retryOptions: new RetryOptions(), + enableEndpointDiscovery: enableEndpointDiscovery, + isThinClientEnabled: false); + CancellationToken cancellationToken = new(); - OperationCanceledException operationCancelledException = new(message: "Operation was cancelled due to cancellation token expiry."); - - GlobalPartitionEndpointManagerCore.PartitionKeyRangeFailoverInfo partitionKeyRangeFailoverInfo = ClientRetryPolicyTests.GetPartitionKeyRangeFailoverInfoUsingReflection( - this.partitionKeyRangeLocationCache, - request.RequestContext.ResolvedPartitionKeyRange, - isReadOnlyOrMultiMasterWriteRequest: isReadOnlyRequest); - - // Validate that the partition key range failover info is not present before the http request exception was captured in the retry policy. - Assert.IsNull(partitionKeyRangeFailoverInfo); - - Task retryStatus; - - // With cancellation token expiry, the retry policy should not failover the offending partition - // until the write threshold is met. - for (int i=0; i< requestThreshold; i++) - { - retryPolicy.OnBeforeSendRequest(request); - retryStatus = retryPolicy.ShouldRetryAsync(operationCancelledException, cancellationToken); - } - - retryStatus = retryPolicy.ShouldRetryAsync(operationCancelledException, cancellationToken); - Assert.IsFalse(retryStatus.Result.ShouldRetry); - - partitionKeyRangeFailoverInfo = ClientRetryPolicyTests.GetPartitionKeyRangeFailoverInfoUsingReflection( - this.partitionKeyRangeLocationCache, - request.RequestContext.ResolvedPartitionKeyRange, - isReadOnlyOrMultiMasterWriteRequest: isReadOnlyRequest); - - if (enablePartitionLevelFailover) - { - // Validate that the partition key range failover info to the next account region is present after the http request exception was captured in the retry policy. - Assert.IsNotNull(partitionKeyRangeFailoverInfo); - Assert.AreEqual(partitionKeyRangeFailoverInfo.Current, readLocations[1]); - } - else - { - Assert.IsNull(partitionKeyRangeFailoverInfo); - } + OperationCanceledException operationCancelledException = new(message: "Operation was cancelled due to cancellation token expiry."); + + GlobalPartitionEndpointManagerCore.PartitionKeyRangeFailoverInfo partitionKeyRangeFailoverInfo = ClientRetryPolicyTests.GetPartitionKeyRangeFailoverInfoUsingReflection( + this.partitionKeyRangeLocationCache, + request.RequestContext.ResolvedPartitionKeyRange, + isReadOnlyOrMultiMasterWriteRequest: isReadOnlyRequest); + + // Validate that the partition key range failover info is not present before the http request exception was captured in the retry policy. + Assert.IsNull(partitionKeyRangeFailoverInfo); + + Task retryStatus; + + // With cancellation token expiry, the retry policy should not failover the offending partition + // until the write threshold is met. + for (int i=0; i< requestThreshold; i++) + { + retryPolicy.OnBeforeSendRequest(request); + retryStatus = retryPolicy.ShouldRetryAsync(operationCancelledException, cancellationToken); + } + + retryStatus = retryPolicy.ShouldRetryAsync(operationCancelledException, cancellationToken); + Assert.IsFalse(retryStatus.Result.ShouldRetry); + + partitionKeyRangeFailoverInfo = ClientRetryPolicyTests.GetPartitionKeyRangeFailoverInfoUsingReflection( + this.partitionKeyRangeLocationCache, + request.RequestContext.ResolvedPartitionKeyRange, + isReadOnlyOrMultiMasterWriteRequest: isReadOnlyRequest); + + if (enablePartitionLevelFailover) + { + // Validate that the partition key range failover info to the next account region is present after the http request exception was captured in the retry policy. + Assert.IsNotNull(partitionKeyRangeFailoverInfo); + Assert.AreEqual(partitionKeyRangeFailoverInfo.Current, readLocations[1]); + } + else + { + Assert.IsNull(partitionKeyRangeFailoverInfo); + } } [TestMethod] @@ -402,7 +400,135 @@ public async Task ClientRetryPolicy_NoRetry_MultiMaster_Read_NoPreferredLocation public async Task ClientRetryPolicy_NoRetry_MultiMaster_Write_NoPreferredLocationsAsync() { await this.ValidateConnectTimeoutTriggersClientRetryPolicyAsync(isReadRequest: false, useMultipleWriteLocations: true, usesPreferredLocations: false, true); - } + } + + /// + /// Test to validate that hub region header is added on 404/1002 for single master accounts only, + /// starting from the second retry (after first retry also fails). For multi-master accounts, + /// the header should NOT be added. + /// + [TestMethod] + [DataRow(true, true, DisplayName = "Read request on single master - Hub region header added after first retry fails")] + [DataRow(false, true, DisplayName = "Write request on single master - Hub region header added after first retry fails")] + [DataRow(true, false, DisplayName = "Read request on multi-master - Hub region header NOT added")] + [DataRow(false, false, DisplayName = "Write request on multi-master - Hub region header NOT added")] + public async Task ClientRetryPolicy_HubRegionHeader_AddedOn404_1002_BasedOnAccountType(bool isReadRequest, bool isSingleMaster) + { + // Arrange + const bool enableEndpointDiscovery = true; + + using GlobalEndpointManager endpointManager = this.Initialize( + useMultipleWriteLocations: !isSingleMaster, + enableEndpointDiscovery: enableEndpointDiscovery, + isPreferredLocationsListEmpty: false, + enforceSingleMasterSingleWriteLocation: isSingleMaster); + + ClientRetryPolicy retryPolicy = new ClientRetryPolicy( + endpointManager, + this.partitionKeyRangeLocationCache, + new RetryOptions(), + enableEndpointDiscovery, + isThinClientEnabled: false); + + DocumentServiceRequest request = this.CreateRequest(isReadRequest: isReadRequest, isMasterResourceType: false); + + // First attempt - header should not exist + retryPolicy.OnBeforeSendRequest(request); + Assert.IsNull(request.Headers.GetValues(HubRegionHeader), "Header should not exist on initial request before any 404/1002 error."); + + // Simulate first 404/1002 error + DocumentClientException sessionNotAvailableException = new DocumentClientException( + message: "Simulated 404/1002 ReadSessionNotAvailable", + innerException: null, + statusCode: HttpStatusCode.NotFound, + substatusCode: SubStatusCodes.ReadSessionNotAvailable, + requestUri: request.RequestContext.LocationEndpointToRoute, + responseHeaders: new DictionaryNameValueCollection()); + + ShouldRetryResult shouldRetry = await retryPolicy.ShouldRetryAsync(sessionNotAvailableException, CancellationToken.None); + Assert.IsTrue(shouldRetry.ShouldRetry, "Should retry on 404/1002."); + + // First retry attempt - header should NOT be present yet + retryPolicy.OnBeforeSendRequest(request); + string[] headerValues = request.Headers.GetValues(HubRegionHeader); + Assert.IsNull(headerValues, "Header should NOT be present on first retry attempt (before it fails)."); + + // Simulate first retry also failing with 404/1002 + DocumentClientException sessionNotAvailableException2 = new DocumentClientException( + message: "Simulated 404/1002 ReadSessionNotAvailable on first retry", + innerException: null, + statusCode: HttpStatusCode.NotFound, + substatusCode: SubStatusCodes.ReadSessionNotAvailable, + requestUri: request.RequestContext.LocationEndpointToRoute, + responseHeaders: new DictionaryNameValueCollection()); + + shouldRetry = await retryPolicy.ShouldRetryAsync(sessionNotAvailableException2, CancellationToken.None); + + if (isSingleMaster) + { + // For single master, after one retry fails with 404/1002, it won't retry further + // But the header flag should be set for any potential future retries due to other errors + Assert.IsFalse(shouldRetry.ShouldRetry, "Single master should not retry again after first 404/1002 retry fails."); + + // The header flag should be set even though no more 404/1002 retries will happen + // This ensures if the request is retried for a different reason (e.g., 503), it will have the header + } + else + { + // Multi-master can retry across multiple regions + Assert.IsTrue(shouldRetry.ShouldRetry, "Multi-master should continue retrying on 404/1002."); + } + + // For single master: Verify header would be added if request is retried for other reasons (e.g., 503) + // For multi-master: Verify header is NOT added even on subsequent retries + if (isSingleMaster) + { + // Simulate a 503 error to trigger another retry + DocumentClientException serviceUnavailableException = new DocumentClientException( + message: "Simulated 503 ServiceUnavailable", + innerException: null, + statusCode: HttpStatusCode.ServiceUnavailable, + substatusCode: SubStatusCodes.Unknown, + requestUri: request.RequestContext.LocationEndpointToRoute, + responseHeaders: new DictionaryNameValueCollection()); + + shouldRetry = await retryPolicy.ShouldRetryAsync(serviceUnavailableException, CancellationToken.None); + + if (shouldRetry.ShouldRetry) + { + // Now verify the header is present on this retry triggered by 503 + retryPolicy.OnBeforeSendRequest(request); + headerValues = request.Headers.GetValues(HubRegionHeader); + Assert.IsNotNull(headerValues, "Header should be present on retry after 404/1002 flag was set."); + Assert.AreEqual(1, headerValues.Length, "Header should have exactly one value."); + Assert.AreEqual(bool.TrueString, headerValues[0], "Header value should be 'True'."); + } + } + else + { + // For multi-master: Verify header is NOT added even on subsequent retries + for (int retryAttempt = 2; retryAttempt <= 3; retryAttempt++) + { + if (shouldRetry.ShouldRetry) + { + retryPolicy.OnBeforeSendRequest(request); + headerValues = request.Headers.GetValues(HubRegionHeader); + Assert.IsNull(headerValues, $"Header should NOT be present on retry attempt {retryAttempt} for multi-master account."); + + // Simulate another 404/1002 or 503 to continue retry loop + DocumentClientException nextException = new DocumentClientException( + message: $"Simulated error on retry {retryAttempt}", + innerException: null, + statusCode: retryAttempt % 2 == 0 ? HttpStatusCode.ServiceUnavailable : HttpStatusCode.NotFound, + substatusCode: retryAttempt % 2 == 0 ? SubStatusCodes.Unknown : SubStatusCodes.ReadSessionNotAvailable, + requestUri: request.RequestContext.LocationEndpointToRoute, + responseHeaders: new DictionaryNameValueCollection()); + + shouldRetry = await retryPolicy.ShouldRetryAsync(nextException, CancellationToken.None); + } + } + } + } private async Task ValidateConnectTimeoutTriggersClientRetryPolicyAsync( bool isReadRequest, @@ -442,15 +568,14 @@ private async Task ValidateConnectTimeoutTriggersClientRetryPolicyAsync( useMultipleWriteLocations: useMultipleWriteLocations, detectClientConnectivityIssues: true, disableRetryWithRetryPolicy: false, - enableReplicaValidation: false, - accountConfigurationProperties: null); + enableReplicaValidation: false); // Reducing retry timeout to avoid long-running tests replicatedResourceClient.GoneAndRetryWithRetryTimeoutInSecondsOverride = 1; this.partitionKeyRangeLocationCache = GlobalPartitionEndpointManagerNoOp.Instance; - - ClientRetryPolicy retryPolicy = new ClientRetryPolicy(mockDocumentClientContext.GlobalEndpointManager, this.partitionKeyRangeLocationCache, new RetryOptions(), enableEndpointDiscovery: true, isPartitionLevelFailoverEnabled: false, false); + + ClientRetryPolicy retryPolicy = new ClientRetryPolicy(mockDocumentClientContext.GlobalEndpointManager, this.partitionKeyRangeLocationCache, new RetryOptions(), enableEndpointDiscovery: true, false); INameValueCollection headers = new DictionaryNameValueCollection(); headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, ConsistencyLevel.BoundedStaleness.ToString()); @@ -517,30 +642,30 @@ await BackoffRetryUtility.ExecuteAsync( } } } - } - - private static GlobalPartitionEndpointManagerCore.PartitionKeyRangeFailoverInfo GetPartitionKeyRangeFailoverInfoUsingReflection( - GlobalPartitionEndpointManager globalPartitionEndpointManager, - PartitionKeyRange pkRange, - bool isReadOnlyOrMultiMasterWriteRequest) - { - string fieldName = isReadOnlyOrMultiMasterWriteRequest ? "PartitionKeyRangeToLocationForReadAndWrite" : "PartitionKeyRangeToLocationForWrite"; - FieldInfo fieldInfo = globalPartitionEndpointManager - .GetType() - .GetField( - name: fieldName, - bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic); - - if (fieldInfo != null) - { - Lazy> partitionKeyRangeToLocation = (Lazy>)fieldInfo.GetValue(globalPartitionEndpointManager); - partitionKeyRangeToLocation.Value.TryGetValue(pkRange, out GlobalPartitionEndpointManagerCore.PartitionKeyRangeFailoverInfo partitionKeyRangeFailoverInfo); - - return partitionKeyRangeFailoverInfo; - } - - return null; - } + } + + private static GlobalPartitionEndpointManagerCore.PartitionKeyRangeFailoverInfo GetPartitionKeyRangeFailoverInfoUsingReflection( + GlobalPartitionEndpointManager globalPartitionEndpointManager, + PartitionKeyRange pkRange, + bool isReadOnlyOrMultiMasterWriteRequest) + { + string fieldName = isReadOnlyOrMultiMasterWriteRequest ? "PartitionKeyRangeToLocationForReadAndWrite" : "PartitionKeyRangeToLocationForWrite"; + FieldInfo fieldInfo = globalPartitionEndpointManager + .GetType() + .GetField( + name: fieldName, + bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic); + + if (fieldInfo != null) + { + Lazy> partitionKeyRangeToLocation = (Lazy>)fieldInfo.GetValue(globalPartitionEndpointManager); + partitionKeyRangeToLocation.Value.TryGetValue(pkRange, out GlobalPartitionEndpointManagerCore.PartitionKeyRangeFailoverInfo partitionKeyRangeFailoverInfo); + + return partitionKeyRangeFailoverInfo; + } + + return null; + } private static AccountProperties CreateDatabaseAccount( bool useMultipleWriteLocations, @@ -634,9 +759,9 @@ private GlobalEndpointManager Initialize( if (enablePartitionLevelFailover) { - this.partitionKeyRangeLocationCache = new GlobalPartitionEndpointManagerCore( - globalEndpointManager: endpointManager, - isPartitionLevelFailoverEnabled: enablePartitionLevelFailover, + this.partitionKeyRangeLocationCache = new GlobalPartitionEndpointManagerCore( + globalEndpointManager: endpointManager, + isPartitionLevelFailoverEnabled: enablePartitionLevelFailover, isPartitionLevelCircuitBreakerEnabled: enablePartitionLevelFailover || enablePartitionLevelCircuitBreaker); } else diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CollectionRoutingMapTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CollectionRoutingMapTest.cs index 7e92be0f9f..ed0f12b209 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CollectionRoutingMapTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CollectionRoutingMapTest.cs @@ -55,7 +55,7 @@ public void TestCollectionRoutingMap() serviceIdentity3), - }, string.Empty); + }, string.Empty, false); Assert.AreEqual("0", routingMap.OrderedPartitionKeyRanges[0].Id); Assert.AreEqual("1", routingMap.OrderedPartitionKeyRanges[1].Id); @@ -116,6 +116,298 @@ public void TestCollectionRoutingMap() Assert.AreEqual("2", partitionKeyRanges1.ElementAt(1).Id); } + /// + /// Validates that CollectionRoutingMap correctly identifies overlapping partition key ranges + /// when using length-aware range comparators. + /// This test ensures that EPK advanced comparison logic are applied as expected, + /// and that the routing map's behavior is consistent regardless if the input EPK is fully or partially specified. + /// The test covers scenarios where input EPKs are partial or fall on range boundaries, + /// verifying that the correct partition key ranges are returned when using the new LengthAware comparators. + /// + [TestMethod] + [DataRow(false)] + [DataRow(true)] + public void TestCollectionRoutingMapWithLengthAwareRangeComparators(bool isRoutingMapFullySpecified) + { + try + { + // Arrange: Set useLengthAwareComparer flag to "true" since the default is only true for Preview. + CollectionRoutingMap routingMap = this.GenerateRoutingMap(isRoutingMapFullySpecified, true); + + // Test scenario 1.1: Input EPK is partial and falls on the boundary between two overlapping ranges. + // The LengthAware comparators are able to correctly compare partial and full EPK ranges.Routing map is hybrid of fully specified and partially specified EPK ranges. + // Input Min EPK 06AB34CFE4E482236BCACBBF50E234AB matches (significant bytes) with maxEPK of pkrangeid 1 and minEPK of pkrangeid 2. + Range inputPkRange = new Range( + "06AB34CFE4E482236BCACBBF50E234AB", + "06AB34CFE4E482236BCACBBF50E234ABFF", + true, + false); + + // Expected outcome: Only partition key range with id 2 overlaps, as the LengthAware comparator correctly handles the partial EPK. + IReadOnlyList partitionKeyRanges1 = routingMap.GetOverlappingRanges(inputPkRange); + Assert.AreEqual(1, partitionKeyRanges1.Count); + Assert.AreEqual("2", partitionKeyRanges1[0].Id); + + // Test scenario 1.2: Input EPK falls on a boundary and maxEPK also matches the next range's max. + // The LengthAware comparator should return only the correct overlapping range. + inputPkRange = new Range( + "0BD3FBE846AF75790CE63F78B1A81631", + "0BD3FBE846AF75790CE63F78B1A81631FF", + true, + false); + + partitionKeyRanges1 = routingMap.GetOverlappingRanges(inputPkRange); + Assert.AreEqual(1, partitionKeyRanges1.Count); + CollectionAssert.AreEquivalent(new[] { "11" }, partitionKeyRanges1.Select(r => r.Id).ToArray()); + + inputPkRange = new Range( + "0D4DC2CD8F49C65A8E0C5306B61B43440D4DC2CD8F49C65A8E0C5306B61B4343", + "0D4DC2CD8F49C65A8E0C5306B61B43440D4DC2CD8F49C65A8E0C5306B61B4344", + true, + false); + + partitionKeyRanges1 = routingMap.GetOverlappingRanges(inputPkRange); + Assert.AreEqual(1, partitionKeyRanges1.Count); + CollectionAssert.AreEquivalent(new[] { "4" }, partitionKeyRanges1.Select(r => r.Id).ToArray()); + + // Test scenario 1.2 (continued): Input EPK falls in boundary and maxEPK also matches the next range's max. + inputPkRange = new Range( + "0BD3FBE846AF75790CE63F78B1A81620", + "0BD3FBE846AF75790CE63F78B1A81631", + true, + false); + + partitionKeyRanges1 = routingMap.GetOverlappingRanges(inputPkRange); + Assert.AreEqual(1, partitionKeyRanges1.Count); + CollectionAssert.AreEquivalent(new[] { "3" }, partitionKeyRanges1.Select(r => r.Id).ToArray()); + + // Test scenario 1.3: Input EPK is partial and spans two overlapping ranges. + /// Input Min EPK 0DCEB8CE51C6BFE84F4BD9409F69B9BB falls in both pkrangeid 4 and pkrangeid 5. + inputPkRange = new Range( + "0DCEB8CE51C6BFE84F4BD9409F69B9BB", + "0DCEB8CE51C6BFE84F4BD9409F69B9BBFF", + true, + false); + + partitionKeyRanges1 = routingMap.GetOverlappingRanges(inputPkRange); + Assert.AreEqual(2, partitionKeyRanges1.Count); + CollectionAssert.AreEquivalent(new[] { "24", "5" }, partitionKeyRanges1.Select(r => r.Id).ToArray()); + + + ///Test scenario 1.4: Input EPK is partial and falls in a single range in the middle. Routing map is hybrid of fully specified and partially specified ranges. + inputPkRange = new Range( + "02559A67F2724111B5E565DFA8711A00", + "02559A67F2724111B5E565DFA8711A00", + true, + true); + + partitionKeyRanges1 = routingMap.GetOverlappingRanges(inputPkRange); + Assert.AreEqual(1, partitionKeyRanges1.Count); + Assert.AreEqual("0", partitionKeyRanges1[0].Id); + + + ///Test scenario 1.5: Input EPK is partial and falls in a single range in the middle. Routing map targeted range has partial EPK values only. + inputPkRange = new Range( + "0D4DC2CD8F49C65A8E0C5306B61B4345", + "0D4DC2CD8F49C65A8E0C5306B61B4345", + true, + true); + + partitionKeyRanges1 = routingMap.GetOverlappingRanges(inputPkRange); + Assert.AreEqual(1, partitionKeyRanges1.Count); + Assert.AreEqual("4", partitionKeyRanges1[0].Id); + + + // The following part of the test case verifies the routing map values i.e.backend ranges when they are not fully specified. + if (!isRoutingMapFullySpecified) + { + // Test scenario 1.6: Input EPK is fully specified and backend range is partially specified. + // The LengthAware comparator correctly matches the fully specified input to the partially specified backend range. + inputPkRange = new Range( + "0D4DC2CD8F49C65A8E0C5306B61B434300000000000000000000000000000000", + "0D4EC2CD8F49C65A8E0C5306B61B434300000000000000000000000000000000", + true, + false); + + // LengthAware comparator yields only the correct range. + partitionKeyRanges1 = routingMap.GetOverlappingRanges(inputPkRange); + Assert.AreEqual(1, partitionKeyRanges1.Count); + CollectionAssert.AreEquivalent(new[] { "4" }, partitionKeyRanges1.Select(r => r.Id).ToArray()); + } + } + finally + { + // Clean up: Remove the environment variable after the test. + Environment.SetEnvironmentVariable(ConfigurationManager.UseLengthAwareRangeComparator, null); + } + } + + // Test GetOverlappingRanges behavior when the UseLengthAwareRangeComparator environment flag is set to false, + // which forces the use of legacy Min/Max comparators. + [TestMethod] + public void TestLegacyComparatorsUsedWhenLengthAwareComparatorFlagIsFalse() + { + try + { + + // Arrange: Set useLengthAwareComparer to false to force legacy comparator usage. + CollectionRoutingMap routingMap = this.GenerateRoutingMap(false, false); + + + // Test scenario: Input EPK is partial and falls on the boundary between two overlapping ranges. + // With the environment flag set, the routing map uses legacy Min/Max comparators, which do not distinguish + // between partial and full EPKs. As a result, both partition key ranges with ids 1 and 2 are considered overlapping. + // Input Min EPK 06AB34CFE4E482236BCACBBF50E234AB matches (significant bytes) with maxEPK of pkrangeid 1 and minEPK of pkrangeid 2. + Range inputPkRange = new Range( + "06AB34CFE4E482236BCACBBF50E234AB", + "06AB34CFE4E482236BCACBBF50E234ABFF", + true, + false); + IReadOnlyList partitionKeyRanges1 = routingMap.GetOverlappingRanges(inputPkRange); + Assert.AreEqual(2, partitionKeyRanges1.Count); + CollectionAssert.AreEquivalent(new[] { "1", "2" }, partitionKeyRanges1.Select(r => r.Id).ToArray()); + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.UseLengthAwareRangeComparator, null); + } + } + + private CollectionRoutingMap GenerateRoutingMap(bool isFullySpecified, bool useLengthAwareComparer) + { + IEnumerable> partitionKeyRangeTuples = new[] + { + Tuple.Create( + new PartitionKeyRange + { + Id = "0", + MinInclusive = "", + MaxExclusive = "03559A67F2724111B5E565DFA8711A00" + }, + (ServiceIdentity)null), + + Tuple.Create( + new PartitionKeyRange + { + Id = "1", + MinInclusive = "03559A67F2724111B5E565DFA8711A00", + MaxExclusive = "06AB34CFE4E482236BCACBBF50E234AB00000000000000000000000000000000" + }, + (ServiceIdentity)null), + + Tuple.Create( + new PartitionKeyRange + { + Id = "2", + MinInclusive = "06AB34CFE4E482236BCACBBF50E234AB00000000000000000000000000000000", + MaxExclusive = "0BD3FBE846AF75790CE63F78B1A81620" + }, + (ServiceIdentity)null), + + Tuple.Create( + new PartitionKeyRange + { + Id = "3", + MinInclusive = "0BD3FBE846AF75790CE63F78B1A81620", + MaxExclusive = "0BD3FBE846AF75790CE63F78B1A8163100000000000000000000000000000000" + }, + (ServiceIdentity)null), + Tuple.Create( + new PartitionKeyRange + { + Id = "11", + MinInclusive = "0BD3FBE846AF75790CE63F78B1A8163100000000000000000000000000000000", + MaxExclusive = "0BD3FBE846AF75790CE63F78B1A81631FF" + }, + (ServiceIdentity)null), + Tuple.Create( + new PartitionKeyRange + { + Id = "12", + MinInclusive = "0BD3FBE846AF75790CE63F78B1A81631FF", + MaxExclusive = "0D4DC2CD8F49C65A8E0C5306B61B4343" + }, + (ServiceIdentity)null), + + Tuple.Create( + new PartitionKeyRange + { + Id = "4", + MinInclusive = "0D4DC2CD8F49C65A8E0C5306B61B4343", + MaxExclusive = "0D4EC2CD8F49C65A8E0C5306B61B4343" + }, + (ServiceIdentity)null), + + Tuple.Create( + new PartitionKeyRange + { + Id = "44", + MinInclusive = "0D4EC2CD8F49C65A8E0C5306B61B4343", + MaxExclusive = "0D5DC2CD8F49C65A8E0C5306B61B4343" + }, + (ServiceIdentity)null), + + Tuple.Create( + new PartitionKeyRange + { + Id = "24", + MinInclusive = "0D5DC2CD8F49C65A8E0C5306B61B4343", + MaxExclusive = "0DCEB8CE51C6BFE84F4BD9409F69B9BB2164DEBD78C50C850E0C1E3E3F0579ED" + }, + (ServiceIdentity)null), + + Tuple.Create( + new PartitionKeyRange + { + Id = "5", + MinInclusive = "0DCEB8CE51C6BFE84F4BD9409F69B9BB2164DEBD78C50C850E0C1E3E3F0579ED", + MaxExclusive = "1080F600C27CF98DC13F8639E94E7676" + }, + (ServiceIdentity)null), + Tuple.Create( + new PartitionKeyRange + { + Id = "9", + MinInclusive = "1080F600C27CF98DC13F8639E94E7676", + MaxExclusive = "FF" + }, + (ServiceIdentity)null), + }; + + if (isFullySpecified) + { + partitionKeyRangeTuples = partitionKeyRangeTuples + .Select(tuple => + { + PartitionKeyRange range = tuple.Item1; + // Pad right to 64 bytes (128 hex chars) for MinInclusive and MaxExclusive if not empty + string PadTo64(string value) + { + if (string.IsNullOrEmpty(value) || value == "FF") + return value; + return value.PadRight(64, '0'); + } + return Tuple.Create( + new PartitionKeyRange + { + Id = range.Id, + MinInclusive = PadTo64(range.MinInclusive), + MaxExclusive = PadTo64(range.MaxExclusive) + }, + tuple.Item2 + ); + }) + .ToList(); + } + + CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap( + partitionKeyRangeTuples, + string.Empty, + useLengthAwareComparer); + + return routingMap; + } + [TestMethod] [ExpectedException(typeof(InvalidOperationException))] public void TestInvalidRoutingMap() @@ -126,7 +418,7 @@ public void TestInvalidRoutingMap() Tuple.Create(new PartitionKeyRange {Id = "1", MinInclusive = "0000000020", MaxExclusive = "0000000030"}, (ServiceIdentity)null), Tuple.Create(new PartitionKeyRange { Id = "2", MinInclusive = "0000000025", MaxExclusive = "0000000035"}, (ServiceIdentity)null), }, - string.Empty); + string.Empty, false); } [TestMethod] @@ -138,7 +430,7 @@ public void TestIncompleteRoutingMap() Tuple.Create(new PartitionKeyRange{ Id = "2", MinInclusive = "", MaxExclusive = "0000000030"}, (ServiceIdentity)null), Tuple.Create(new PartitionKeyRange{ Id = "3", MinInclusive = "0000000031", MaxExclusive = "FF"}, (ServiceIdentity)null), }, - string.Empty); + string.Empty, false); Assert.IsNull(routingMap); @@ -148,7 +440,7 @@ public void TestIncompleteRoutingMap() Tuple.Create(new PartitionKeyRange{Id = "2", MinInclusive = "", MaxExclusive = "0000000030"}, (ServiceIdentity)null), Tuple.Create(new PartitionKeyRange{Id = "3", MinInclusive = "0000000030", MaxExclusive = "FF"}, (ServiceIdentity)null), }, - string.Empty); + string.Empty, false); Assert.IsNotNull(routingMap); } @@ -163,7 +455,7 @@ public void TestGoneRanges() Tuple.Create(new PartitionKeyRange{ Id = "3", MinInclusive = "0000000030", MaxExclusive = "0000000032", Parents = new Collection{"5"}}, (ServiceIdentity)null), Tuple.Create(new PartitionKeyRange{ Id = "4", MinInclusive = "0000000032", MaxExclusive = "FF"}, (ServiceIdentity)null), }, - string.Empty); + string.Empty, false); Assert.IsTrue(routingMap.IsGone("1")); Assert.IsTrue(routingMap.IsGone("0")); @@ -208,7 +500,7 @@ public void TestTryCombineRanges() MinInclusive = "0000000070", MaxExclusive = "FF"}, (ServiceIdentity)null), - }, string.Empty); + }, string.Empty, false); CollectionRoutingMap newRoutingMap = routingMap.TryCombine( new[] @@ -229,7 +521,7 @@ public void TestTryCombineRanges() MaxExclusive = "0000000030"}, (ServiceIdentity)null), }, - null); + null, false); Assert.IsNotNull(newRoutingMap); @@ -268,7 +560,7 @@ public void TestTryCombineRanges() MaxExclusive = "0000000030"}, (ServiceIdentity)null), }, - null); + null, false); Assert.IsNotNull(newRoutingMap); @@ -283,7 +575,7 @@ public void TestTryCombineRanges() MaxExclusive = "0000000002"}, (ServiceIdentity)null), }, - null); + null, false); Assert.IsNull(newRoutingMap); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractEnforcement.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractEnforcement.cs index 2fb714bee5..8092ab7f34 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractEnforcement.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractEnforcement.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Reflection; + using System.Runtime.Versioning; using System.Text.RegularExpressions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; @@ -15,14 +16,108 @@ public class ContractEnforcement { private static readonly InvariantComparer invariantComparer = new(); + private const string ContractsFolder = "Contracts/"; + + /// + /// Gets the current .NET major version from the executing test assembly's target framework. + /// + /// The major version number (e.g., 6 for net6.0, 8 for net8.0), or null if unable to determine. + public static int? GetCurrentMajorVersion() + { + // Read the TFM from the current test assembly TargetFrameworkAttribute + TargetFrameworkAttribute attr = Assembly.GetExecutingAssembly().GetCustomAttribute(); + if (attr?.FrameworkName == null) + { + return null; + } + + // Example: ".NETCoreApp,Version=v8.0" -> 8 + FrameworkName fx = new FrameworkName(attr.FrameworkName); + return fx.Version.Major; + } private static Assembly GetAssemblyLocally(string name) { Assembly.Load(name); Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); - return loadedAssemblies + + // Get the target framework of the currently executing test assembly + Assembly testAssembly = Assembly.GetExecutingAssembly(); + TargetFrameworkAttribute testTfmAttr = testAssembly.GetCustomAttribute(); + string testTfmName = testTfmAttr?.FrameworkName; + + // Find all matching assemblies + Assembly[] matchingAssemblies = loadedAssemblies .Where((candidate) => candidate.FullName.Contains(name + ",")) - .FirstOrDefault(); + .ToArray(); + + if (matchingAssemblies.Length == 0) + { + return null; + } + + // If we have multiple matches and know our test TFM, try to find the best match + if (matchingAssemblies.Length > 1 && !string.IsNullOrEmpty(testTfmName)) + { + // Try to find an assembly with matching or compatible TFM + foreach (Assembly candidate in matchingAssemblies) + { + TargetFrameworkAttribute candidateTfmAttr = candidate.GetCustomAttribute(); + string candidateTfmName = candidateTfmAttr?.FrameworkName; + + // Direct match or compatible framework + if (candidateTfmName == testTfmName || + IsCompatibleFramework(candidateTfmName, testTfmName)) + { + return candidate; + } + } + } + + // Fallback to first match + return matchingAssemblies.FirstOrDefault(); + } + + /// + /// Determines if the candidate framework is compatible with the test framework. + /// For example, netstandard2.0 is compatible with net6.0 or net8.0. + /// + private static bool IsCompatibleFramework(string candidateFramework, string testFramework) + { + if (string.IsNullOrEmpty(candidateFramework) || string.IsNullOrEmpty(testFramework)) + { + return false; + } + + try + { + FrameworkName candidateFn = new FrameworkName(candidateFramework); + FrameworkName testFn = new FrameworkName(testFramework); + + // If candidate is .NETStandard, it's compatible with .NETCoreApp + if (candidateFn.Identifier == ".NETStandard" && testFn.Identifier == ".NETCoreApp") + { + return true; + } + + // Same framework identifier + if (candidateFn.Identifier == testFn.Identifier) + { + // For .NETCoreApp, prefer exact or higher version match + if (testFn.Identifier == ".NETCoreApp") + { + return candidateFn.Version.Major == testFn.Version.Major; + } + return true; + } + } + catch + { + // If framework name parsing fails, fall back to false + return false; + } + + return false; } private sealed class MemberMetadata @@ -66,6 +161,71 @@ private static IEnumerable RemoveDebugSpecificAttributes(IE ); } + /// + /// Generates a normalized, deterministic string representation of a CustomAttributeData. + /// This ensures that attribute parameters are always in the same order, making the + /// contract comparison machine-agnostic and independent of .NET reflection ordering. + /// + private static string NormalizeCustomAttributeString(CustomAttributeData attributeData) + { + // Start with the attribute type name + string result = attributeData.AttributeType.ToString(); + + // Build lists of constructor args and named args + List parts = new List(); + + // Add constructor arguments in order (these are positional, so order matters) + if (attributeData.ConstructorArguments.Count > 0) + { + foreach (CustomAttributeTypedArgument arg in attributeData.ConstructorArguments) + { + parts.Add(ContractEnforcement.FormatAttributeValue(arg)); + } + } + + // Add named arguments (properties/fields) in sorted order for determinism + if (attributeData.NamedArguments.Count > 0) + { + List namedArgs = new List(); + foreach (CustomAttributeNamedArgument namedArg in attributeData.NamedArguments) + { + namedArgs.Add($"{namedArg.MemberName} = {ContractEnforcement.FormatAttributeValue(namedArg.TypedValue)}"); + } + namedArgs.Sort(StringComparer.Ordinal); + parts.AddRange(namedArgs); + } + + // Always add parentheses for consistency, even if there are no arguments + result += "(" + string.Join(", ", parts) + ")"; + + return result; + } + + /// + /// Formats an attribute value for consistent string representation. + /// + private static string FormatAttributeValue(CustomAttributeTypedArgument arg) + { + return arg.Value switch + { + null => "null", + string stringValue => $"\"{stringValue}\"", + Type typeValue => $"typeof({typeValue})", + _ when arg.ArgumentType.IsEnum => ContractEnforcement.FormatEnumValue(arg), + _ when arg.ArgumentType.IsPrimitive => $"({arg.ArgumentType.Name}){arg.Value}", + _ => arg.Value.ToString() + }; + } + + /// + /// Formats an enum value including both the enum name and numeric value. + /// + private static string FormatEnumValue(CustomAttributeTypedArgument arg) + { + string enumName = Enum.GetName(arg.ArgumentType, arg.Value) ?? arg.Value.ToString(); + return $"{arg.ArgumentType.Name}.{enumName} = {arg.Value}"; + } + private static string GenerateNameWithClassAttributes(Type type) { // FullName contains unwanted assembly artifacts like version when it has a generic type @@ -97,9 +257,40 @@ private static string GenerateNameWithClassAttributes(Type type) } + /// + /// Normalizes the string representation of a MemberInfo to ensure machine-agnostic output. + /// For static methods, ensures the calling convention uses '.' (dot notation). + /// For generic methods, includes the generic type parameters in the signature. + /// + private static string NormalizeMemberInfoString(MemberInfo memberInfo) + { + // Only MethodBase (MethodInfo and ConstructorInfo) has calling convention representation issues + if (memberInfo is MethodBase methodBase && methodBase.IsStatic && methodBase.DeclaringType != null) + { + // Normalize to always use ClassName.MethodName format for static methods + string declaringTypeName = methodBase.DeclaringType.FullName ?? methodBase.DeclaringType.Name; + string returnType = methodBase is MethodInfo mi ? mi.ReturnType.ToString() : "Void"; + string methodName = methodBase.Name; + + // Include generic type parameters for generic methods + if (methodBase.IsGenericMethod) + { + Type[] genericArgs = methodBase.GetGenericArguments(); + string genericParams = string.Join(", ", genericArgs.Select(t => t.Name)); + methodName = $"{methodName}[{genericParams}]"; + } + + string parameters = string.Join(", ", methodBase.GetParameters().Select(p => p.ParameterType.ToString())); + + return $"{returnType} {declaringTypeName}.{methodName}({parameters})"; + } + + return memberInfo.ToString(); + } + private static string GenerateNameWithMethodAttributes(MethodInfo methodInfo) { - return $"{methodInfo};{nameof(methodInfo.IsAbstract)}:{(methodInfo.IsAbstract ? bool.TrueString : bool.FalseString)};" + + return $"{ContractEnforcement.NormalizeMemberInfoString(methodInfo)};{nameof(methodInfo.IsAbstract)}:{(methodInfo.IsAbstract ? bool.TrueString : bool.FalseString)};" + $"{nameof(methodInfo.IsStatic)}:{(methodInfo.IsStatic ? bool.TrueString : bool.FalseString)};" + $"{nameof(methodInfo.IsVirtual)}:{(methodInfo.IsVirtual ? bool.TrueString : bool.FalseString)};" + $"{nameof(methodInfo.IsGenericMethod)}:{(methodInfo.IsGenericMethod ? bool.TrueString : bool.FalseString)};" + @@ -109,7 +300,7 @@ private static string GenerateNameWithMethodAttributes(MethodInfo methodInfo) private static string GenerateNameWithPropertyAttributes(PropertyInfo propertyInfo) { - string name = $"{propertyInfo};{nameof(propertyInfo.CanRead)}:{(propertyInfo.CanRead ? bool.TrueString : bool.FalseString)};" + + string name = $"{ContractEnforcement.NormalizeMemberInfoString(propertyInfo)};{nameof(propertyInfo.CanRead)}:{(propertyInfo.CanRead ? bool.TrueString : bool.FalseString)};" + $"{nameof(propertyInfo.CanWrite)}:{(propertyInfo.CanWrite ? bool.TrueString : bool.FalseString)};"; MethodInfo getMethodInfo = propertyInfo.GetGetMethod(); @@ -129,7 +320,7 @@ private static string GenerateNameWithPropertyAttributes(PropertyInfo propertyIn private static string GenerateNameWithFieldAttributes(FieldInfo fieldInfo) { - return $"{fieldInfo};{nameof(fieldInfo.IsInitOnly)}:{(fieldInfo.IsInitOnly ? bool.TrueString : bool.FalseString)};" + + return $"{ContractEnforcement.NormalizeMemberInfoString(fieldInfo)};{nameof(fieldInfo.IsInitOnly)}:{(fieldInfo.IsInitOnly ? bool.TrueString : bool.FalseString)};" + $"{nameof(fieldInfo.IsStatic)}:{(fieldInfo.IsStatic ? bool.TrueString : bool.FalseString)};"; } @@ -143,7 +334,9 @@ private static TypeTree BuildTypeTree(TypeTree root, Type[] types, BindingFlags IEnumerable> memberInfos = root.Type.GetMembers(bindingflags) - .Select(memberInfo => new KeyValuePair($"{memberInfo}{string.Join("-", ContractEnforcement.RemoveDebugSpecificAttributes(memberInfo.CustomAttributes))}", memberInfo)) + .Select(memberInfo => new KeyValuePair( + $"{NormalizeMemberInfoString(memberInfo)}{string.Join("-", ContractEnforcement.RemoveDebugSpecificAttributes(memberInfo.CustomAttributes).Select(attr => "[" + NormalizeCustomAttributeString(attr) + "]"))}", + memberInfo)) .OrderBy(o => o.Key, invariantComparer); foreach (KeyValuePair memberInfo in memberInfos) { @@ -171,7 +364,7 @@ private static TypeTree BuildTypeTree(TypeTree root, Type[] types, BindingFlags } else if (memberInfo.Value.MemberType == MemberTypes.Constructor || memberInfo.Value.MemberType == MemberTypes.Event) { - methodSignature = memberInfo.ToString(); + methodSignature = ContractEnforcement.NormalizeMemberInfoString(memberInfo.Value); } // Certain custom attributes add the following to the string value "d__9" which sometimes changes @@ -194,52 +387,71 @@ private static TypeTree BuildTypeTree(TypeTree root, Type[] types, BindingFlags return root; } - public static void ValidateContractContainBreakingChanges( + /// + /// Validates contract changes using framework-specific baselines with automatic path construction. + /// Determines the current .NET version and builds file paths from patterns. + /// + /// The name of the DLL to validate + /// The type of contract to validate (Standard, Telemetry, or Preview) + /// The baseline file name pattern (e.g., "DotNetSDKAPI", "DotNetSDKTelemetryAPI") + /// The breaking changes file name pattern (e.g., "DotNetSDKAPIChanges") + /// For Preview contracts only: the official baseline pattern (e.g., "DotNetSDKAPI") + public static void ValidateContract( string dllName, - string baselinePath, - string breakingChangesPath) + ContractType contractType, + string baselinePattern, + string breakingChangesPattern, + string officialBaselinePattern = null) { - string localJson = GetCurrentContract(dllName); - File.WriteAllText($"Contracts/{breakingChangesPath}", localJson); + int? currentMajorVersion = GetCurrentMajorVersion(); + if (!currentMajorVersion.HasValue) + { + Assert.Fail("Unable to determine target framework version. Framework-specific contract baselines are required."); + } + + string baselinePath = $"{baselinePattern}.net{currentMajorVersion}.json"; + string breakingChangesPath = $"{breakingChangesPattern}.net{currentMajorVersion}.json"; + string currentJson = GetContractJson(dllName, contractType); + + if (contractType == ContractType.Preview) + { + currentJson = FilterPreviewContract(currentJson, officialBaselinePattern, currentMajorVersion.Value); + } + + File.WriteAllText($"{ContractsFolder}{breakingChangesPath}", currentJson); string baselineJson = GetBaselineContract(baselinePath); - ContractEnforcement.ValidateJsonAreSame(baselineJson, localJson); + ValidateJsonAreSame(baselineJson, currentJson); } - public static void ValidateTelemetryContractContainBreakingChanges( - string dllName, - string baselinePath, - string breakingChangesPath) + private static string GetContractJson(string dllName, ContractType contractType) { - string localTelemetryJson = GetCurrentTelemetryContract(dllName); - File.WriteAllText($"Contracts/{breakingChangesPath}", localTelemetryJson); - - string telemetryBaselineJson = GetBaselineContract(baselinePath); - ContractEnforcement.ValidateJsonAreSame(localTelemetryJson, telemetryBaselineJson); + return contractType switch + { + ContractType.Standard => GetCurrentContract(dllName), + ContractType.Telemetry => GetCurrentTelemetryContract(dllName), + ContractType.Preview => GetCurrentContract(dllName), + _ => throw new ArgumentException($"Unknown contract type: {contractType}", nameof(contractType)) + }; } - public static void ValidatePreviewContractContainBreakingChanges( - string dllName, - string officialBaselinePath, - string previewBaselinePath, - string previewBreakingChangesPath) + private static string FilterPreviewContract(string currentJson, string officialBaselinePattern, int currentMajorVersion) { - string currentPreviewJson = ContractEnforcement.GetCurrentContract( - dllName); - - JObject currentJObject = JObject.Parse(currentPreviewJson); - JObject officialBaselineJObject = JObject.Parse(File.ReadAllText("Contracts/" + officialBaselinePath)); - - string currentJsonNoOfficialContract = ContractEnforcement.RemoveDuplicateContractElements( - localContract: currentJObject, - officialContract: officialBaselineJObject); + if (string.IsNullOrEmpty(officialBaselinePattern)) + { + throw new ArgumentException( + "officialBaselinePattern is required for Preview contract validation", + nameof(officialBaselinePattern)); + } - Assert.IsNotNull(currentJsonNoOfficialContract); + string officialBaselinePath = $"{officialBaselinePattern}.net{currentMajorVersion}.json"; + JObject currentContract = JObject.Parse(currentJson); + JObject officialContract = JObject.Parse(File.ReadAllText($"{ContractsFolder}{officialBaselinePath}")); - string baselinePreviewJson = ContractEnforcement.GetBaselineContract(previewBaselinePath); - File.WriteAllText($"Contracts/{previewBreakingChangesPath}", currentJsonNoOfficialContract); + string filteredJson = RemoveDuplicateContractElements(currentContract, officialContract); + Assert.IsNotNull(filteredJson); - ContractEnforcement.ValidateJsonAreSame(baselinePreviewJson, currentJsonNoOfficialContract); + return filteredJson; } public static string GetCurrentContract(string dllName) @@ -280,7 +492,7 @@ public static string GetCurrentTelemetryContract(string dllName) public static string GetBaselineContract(string baselinePath) { - string baselineFile = File.ReadAllText("Contracts/" + baselinePath); + string baselineFile = File.ReadAllText($"{ContractsFolder}{baselinePath}"); return NormalizeJsonString(baselineFile); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractEnforcementTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractEnforcementTests.cs index 45e45079aa..8e7623d68a 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractEnforcementTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractEnforcementTests.cs @@ -3,6 +3,8 @@ using System; using System.Diagnostics; using System.IO; + using System.Reflection; + using System.Runtime.Versioning; using System.Xml; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -12,36 +14,132 @@ public class ContractEnforcementTests { private const string DllName = "Microsoft.Azure.Cosmos.Client"; - private const string OfficialBaselinePath = "DotNetSDKAPI.json"; - private const string OfficialTelemetryBaselinePath = "DotNetSDKTelemetryAPI.json"; #if PREVIEW [TestMethod] public void PreviewContractChanges() { - ContractEnforcement.ValidatePreviewContractContainBreakingChanges( + ContractEnforcement.ValidateContract( dllName: DllName, - officialBaselinePath: OfficialBaselinePath, - previewBaselinePath: "DotNetPreviewSDKAPI.json", - previewBreakingChangesPath: "DotNetPreviewSDKAPIChanges.json"); + contractType: ContractType.Preview, + baselinePattern: "DotNetPreviewSDKAPI", + breakingChangesPattern: "DotNetPreviewSDKAPIChanges", + officialBaselinePattern: "DotNetSDKAPI"); } #else + /// + /// This test validates the public API surface against a baseline contract. + /// + /// IMPORTANT: Because tests run on multiple .NET versions (net6.0 and net8.0), + /// the contract validation uses framework-specific baselines to ensure consistency: + /// + /// - When running on net6.0: validates against DotNetSDKAPI.net6.json + /// - When running on net8.0: validates against DotNetSDKAPI.net8.json + /// + /// To update baselines, run: UpdateContracts.ps1 from the repository root. + /// This script runs tests on BOTH net6.0 and net8.0 to generate both baselines. + /// [TestMethod] public void ContractChanges() { - ContractEnforcement.ValidateContractContainBreakingChanges( + ContractEnforcement.ValidateContract( dllName: DllName, - baselinePath: OfficialBaselinePath, - breakingChangesPath: "DotNetSDKAPIChanges.json"); + contractType: ContractType.Standard, + baselinePattern: "DotNetSDKAPI", + breakingChangesPattern: "DotNetSDKAPIChanges"); } [TestMethod] public void TelemetryContractChanges() { - ContractEnforcement.ValidateTelemetryContractContainBreakingChanges( + ContractEnforcement.ValidateContract( dllName: DllName, - baselinePath: OfficialTelemetryBaselinePath, - breakingChangesPath: "DotNetSDKTelemetryAPIChanges.json"); + contractType: ContractType.Telemetry, + baselinePattern: "DotNetSDKTelemetryAPI", + breakingChangesPattern: "DotNetSDKTelemetryAPIChanges"); + } + + /// + /// This test validates that the contract enforcement is actually checking for differences + /// by verifying that mismatched JSON strings are properly detected and result in a failure. + /// This ensures that the ValidateJsonAreSame method is working correctly. + /// + [TestMethod] + public void ContractEnforcementDetectsChanges() + { + // Arrange: Create two different JSON strings representing API contracts + string baselineJson = @"{ + ""Subclasses"": { + ""Microsoft.Azure.Cosmos.Container"": { + ""Subclasses"": {}, + ""Members"": { + ""Microsoft.Azure.Cosmos.Container Database"": { + ""Type"": ""Property"", + ""Attributes"": [], + ""MethodInfo"": null + } + } + } + } +}"; + + string modifiedJson = @"{ + ""Subclasses"": { + ""Microsoft.Azure.Cosmos.Container"": { + ""Subclasses"": {}, + ""Members"": { + ""Microsoft.Azure.Cosmos.Container Database"": { + ""Type"": ""Property"", + ""Attributes"": [], + ""MethodInfo"": null + }, + ""Microsoft.Azure.Cosmos.Container NewProperty"": { + ""Type"": ""Property"", + ""Attributes"": [], + ""MethodInfo"": null + } + } + } + } +}"; + + // Act & Assert: Verify that ValidateJsonAreSame throws when contracts differ + AssertFailedException exception = Assert.ThrowsException( + () => ContractEnforcement.ValidateJsonAreSame(baselineJson, modifiedJson)); + + // Verify the exception message mentions the contract update script + Assert.IsTrue(exception.Message.Contains("Public API has changed"), + $"Expected failure message to mention API changes. Actual message: {exception.Message}"); + Assert.IsTrue(exception.Message.Contains("UpdateContracts.ps1"), + $"Expected failure message to mention UpdateContracts.ps1. Actual message: {exception.Message}"); + } + + /// + /// This test validates that identical JSON contracts pass validation, + /// ensuring that the contract enforcement doesn't produce false positives. + /// + [TestMethod] + public void ContractEnforcementAllowsIdenticalContracts() + { + // Arrange: Create identical JSON strings + string json = @"{ + ""Subclasses"": { + ""Microsoft.Azure.Cosmos.Container"": { + ""Subclasses"": {}, + ""Members"": { + ""Microsoft.Azure.Cosmos.Container Database"": { + ""Type"": ""Property"", + ""Attributes"": [], + ""MethodInfo"": null + } + } + } + } +}"; + + // Act & Assert: Verify that identical contracts don't throw + ContractEnforcement.ValidateJsonAreSame(json, json); + // If we get here without an exception, the test passes } #endif @@ -52,7 +150,7 @@ public void UniqueKeyUnsealed() } [TestMethod] - public void ValdatePacakgeVersions() + public void ValdatePackageVersions() { const String VersionFile = "Directory.Build.props"; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractType.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractType.cs new file mode 100644 index 0000000000..0942b131db --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/ContractType.cs @@ -0,0 +1,17 @@ +namespace Microsoft.Azure.Cosmos.Tests.Contracts +{ + /// + /// Represents the type of contract validation to perform. + /// + public enum ContractType + { + /// Standard public API contract + Standard, + + /// Telemetry API contract + Telemetry, + + /// Preview API contract (requires official baseline for comparison) + Preview + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DirectContractTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DirectContractTests.cs index bd2d6c78f0..d73a2d802e 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DirectContractTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DirectContractTests.cs @@ -41,6 +41,17 @@ public void TestInteropTest() Marshal.Release(tryCreateServiceProvider.Result); } + [TestMethod] + public void PublicDirectTypes() + { + Assembly directAssembly = typeof(IStoreClient).Assembly; + + Assert.IsTrue(directAssembly.FullName.StartsWith("Microsoft.Azure.Cosmos.Direct", System.StringComparison.Ordinal), directAssembly.FullName); + + Type[] exportedTypes = directAssembly.GetExportedTypes(); + Assert.AreEqual(0, exportedTypes.Length, string.Join(",", exportedTypes.Select(e => e.Name).ToArray())); + } + [TestMethod] public void MappedRegionsTest() { @@ -118,9 +129,6 @@ public void ProjectPackageDependenciesTest() { "Microsoft.Bcl.HashCode", new Version(1, 1, 0) }, { "Azure.Core", new Version(1, 44, 1) }, { "System.Diagnostics.DiagnosticSource", new Version(8, 0, 1) }, - { "System.Net.Http", new Version(4, 3, 4) }, - { "System.Text.RegularExpressions", new Version(4, 3, 1) }, - { "System.Diagnostics.PerformanceCounter", new Version(6, 0, 0) } }; Assert.AreEqual(projectDependencies.Count, baselineDependencies.Count); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json new file mode 100644 index 0000000000..39964c87a1 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json @@ -0,0 +1,1577 @@ +{ + "Subclasses": { + "Microsoft.Azure.Cosmos.ChangeFeedItem`1;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:True;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.ChangeFeedMetadata get_Metadata()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMetadata get_Metadata();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.ChangeFeedMetadata Metadata[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"metadata\")]-[System.Text.Json.Serialization.JsonPropertyNameAttribute(\"metadata\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute", + "JsonPropertyNameAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMetadata Metadata;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.ChangeFeedMetadata get_Metadata();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Metadata(Microsoft.Azure.Cosmos.ChangeFeedMetadata);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "T Current[Newtonsoft.Json.JsonPropertyAttribute(PropertyName = \"current\")]-[System.Text.Json.Serialization.JsonPropertyNameAttribute(\"current\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute", + "JsonPropertyNameAttribute" + ], + "MethodInfo": "T Current;CanRead:True;CanWrite:True;T get_Current();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Current(T);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "T get_Current()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "T get_Current();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "T get_Previous()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "T get_Previous();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "T Previous[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"previous\")]-[System.Text.Json.Serialization.JsonPropertyNameAttribute(\"previous\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute", + "JsonPropertyNameAttribute" + ], + "MethodInfo": "T Previous;CanRead:True;CanWrite:True;T get_Previous();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Previous(T);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + }, + "Void set_Current(T)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_Current(T);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_Metadata(Microsoft.Azure.Cosmos.ChangeFeedMetadata)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_Metadata(Microsoft.Azure.Cosmos.ChangeFeedMetadata);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_Previous(T)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_Previous(T);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.ChangeFeedMetadata;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Boolean get_IsTimeToLiveExpired()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Boolean get_IsTimeToLiveExpired();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Boolean IsTimeToLiveExpired[System.Text.Json.Serialization.JsonIncludeAttribute()]-[System.Text.Json.Serialization.JsonPropertyNameAttribute(\"timeToLiveExpired\")]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"timeToLiveExpired\")]": { + "Type": "Property", + "Attributes": [ + "JsonIncludeAttribute", + "JsonPropertyAttribute", + "JsonPropertyNameAttribute" + ], + "MethodInfo": "Boolean IsTimeToLiveExpired;CanRead:True;CanWrite:True;Boolean get_IsTimeToLiveExpired();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int64 get_Lsn()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Int64 get_Lsn();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int64 get_PreviousLsn()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Int64 get_PreviousLsn();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int64 Lsn[System.Text.Json.Serialization.JsonIncludeAttribute()]-[System.Text.Json.Serialization.JsonPropertyNameAttribute(\"lsn\")]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"lsn\")]": { + "Type": "Property", + "Attributes": [ + "JsonIncludeAttribute", + "JsonPropertyAttribute", + "JsonPropertyNameAttribute" + ], + "MethodInfo": "Int64 Lsn;CanRead:True;CanWrite:True;Int64 get_Lsn();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int64 PreviousLsn[System.Text.Json.Serialization.JsonIncludeAttribute()]-[System.Text.Json.Serialization.JsonPropertyNameAttribute(\"previousImageLSN\")]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"previousImageLSN\")]": { + "Type": "Property", + "Attributes": [ + "JsonIncludeAttribute", + "JsonPropertyAttribute", + "JsonPropertyNameAttribute" + ], + "MethodInfo": "Int64 PreviousLsn;CanRead:True;CanWrite:True;Int64 get_PreviousLsn();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.ChangeFeedOperationType get_OperationType()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedOperationType get_OperationType();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.ChangeFeedOperationType OperationType[Newtonsoft.Json.JsonConverterAttribute(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]-[System.Text.Json.Serialization.JsonIncludeAttribute()]-[System.Text.Json.Serialization.JsonPropertyNameAttribute(\"operationType\")]-[System.Text.Json.Serialization.JsonConverterAttribute(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"operationType\")]": { + "Type": "Property", + "Attributes": [ + "JsonConverterAttribute", + "JsonConverterAttribute", + "JsonIncludeAttribute", + "JsonPropertyAttribute", + "JsonPropertyNameAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedOperationType OperationType;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.ChangeFeedOperationType get_OperationType();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.Dictionary`2[System.String,System.Object] get_PartitionKey()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Collections.Generic.Dictionary`2[System.String,System.Object] get_PartitionKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.Dictionary`2[System.String,System.Object] PartitionKey[System.Text.Json.Serialization.JsonIncludeAttribute()]-[System.Text.Json.Serialization.JsonPropertyNameAttribute(\"partitionKey\")]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"partitionKey\")]": { + "Type": "Property", + "Attributes": [ + "JsonIncludeAttribute", + "JsonPropertyAttribute", + "JsonPropertyNameAttribute" + ], + "MethodInfo": "System.Collections.Generic.Dictionary`2[System.String,System.Object] PartitionKey;CanRead:True;CanWrite:True;System.Collections.Generic.Dictionary`2[System.String,System.Object] get_PartitionKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.DateTime ConflictResolutionTimestamp[System.Text.Json.Serialization.JsonIgnoreAttribute()]-[Newtonsoft.Json.JsonIgnoreAttribute()]": { + "Type": "Property", + "Attributes": [ + "JsonIgnoreAttribute", + "JsonIgnoreAttribute" + ], + "MethodInfo": "System.DateTime ConflictResolutionTimestamp;CanRead:True;CanWrite:False;System.DateTime get_ConflictResolutionTimestamp();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.DateTime get_ConflictResolutionTimestamp()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.DateTime get_ConflictResolutionTimestamp();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_Id()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String Id[System.Text.Json.Serialization.JsonIncludeAttribute()]-[System.Text.Json.Serialization.JsonPropertyNameAttribute(\"id\")]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"id\")]": { + "Type": "Property", + "Attributes": [ + "JsonIncludeAttribute", + "JsonPropertyAttribute", + "JsonPropertyNameAttribute" + ], + "MethodInfo": "System.String Id;CanRead:True;CanWrite:True;System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.ChangeFeedMode;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.ChangeFeedMode AllVersionsAndDeletes": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMode AllVersionsAndDeletes;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.ChangeFeedMode Microsoft.Azure.Cosmos.ChangeFeedMode.get_AllVersionsAndDeletes();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.ChangeFeedMode Microsoft.Azure.Cosmos.ChangeFeedMode.get_AllVersionsAndDeletes()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMode Microsoft.Azure.Cosmos.ChangeFeedMode.get_AllVersionsAndDeletes();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.ChangeFeedOperationType;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Int32 value__": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Int32 value__;IsInitOnly:False;IsStatic:False;" + }, + "Microsoft.Azure.Cosmos.ChangeFeedOperationType Create[System.Runtime.Serialization.EnumMemberAttribute(Value = \"create\")]": { + "Type": "Field", + "Attributes": [ + "EnumMemberAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedOperationType Create;IsInitOnly:False;IsStatic:True;" + }, + "Microsoft.Azure.Cosmos.ChangeFeedOperationType Delete[System.Runtime.Serialization.EnumMemberAttribute(Value = \"delete\")]": { + "Type": "Field", + "Attributes": [ + "EnumMemberAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedOperationType Delete;IsInitOnly:False;IsStatic:True;" + }, + "Microsoft.Azure.Cosmos.ChangeFeedOperationType Replace[System.Runtime.Serialization.EnumMemberAttribute(Value = \"replace\")]": { + "Type": "Field", + "Attributes": [ + "EnumMemberAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedOperationType Replace;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.ChangeFeedPolicy;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.TimeSpan FullFidelityNoRetention": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.TimeSpan FullFidelityNoRetention;CanRead:True;CanWrite:False;System.TimeSpan Microsoft.Azure.Cosmos.ChangeFeedPolicy.get_FullFidelityNoRetention();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.TimeSpan FullFidelityRetention[Newtonsoft.Json.JsonIgnoreAttribute()]": { + "Type": "Property", + "Attributes": [ + "JsonIgnoreAttribute" + ], + "MethodInfo": "System.TimeSpan FullFidelityRetention;CanRead:True;CanWrite:True;System.TimeSpan get_FullFidelityRetention();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_FullFidelityRetention(System.TimeSpan);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.TimeSpan get_FullFidelityRetention()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.TimeSpan get_FullFidelityRetention();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.TimeSpan Microsoft.Azure.Cosmos.ChangeFeedPolicy.get_FullFidelityNoRetention()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.TimeSpan Microsoft.Azure.Cosmos.ChangeFeedPolicy.get_FullFidelityNoRetention();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + }, + "Void set_FullFidelityRetention(System.TimeSpan)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void set_FullFidelityRetention(System.TimeSpan);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Container;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes[T](System.String, ChangeFeedHandler`1)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes[T](System.String, ChangeFeedHandler`1);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.SemanticRerankResult] SemanticRerankAsync(System.String, System.Collections.Generic.IEnumerable`1[System.String], System.Collections.Generic.IDictionary`2[System.String,System.Object], System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.SemanticRerankResult] SemanticRerankAsync(System.String, System.Collections.Generic.IEnumerable`1[System.String], System.Collections.Generic.IDictionary`2[System.String,System.Object], System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Boolean] IsFeedRangePartOfAsync(Microsoft.Azure.Cosmos.FeedRange, Microsoft.Azure.Cosmos.FeedRange, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Boolean] IsFeedRangePartOfAsync(Microsoft.Azure.Cosmos.FeedRange, Microsoft.Azure.Cosmos.FeedRange, System.Threading.CancellationToken);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Collections.Generic.IEnumerable`1[System.String]] GetPartitionKeyRangesAsync(Microsoft.Azure.Cosmos.FeedRange, System.Threading.CancellationToken)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Collections.Generic.IEnumerable`1[System.String]] GetPartitionKeyRangesAsync(Microsoft.Azure.Cosmos.FeedRange, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.ContainerProperties;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.ChangeFeedPolicy ChangeFeedPolicy[Newtonsoft.Json.JsonIgnoreAttribute()]": { + "Type": "Property", + "Attributes": [ + "JsonIgnoreAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedPolicy ChangeFeedPolicy;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.ChangeFeedPolicy get_ChangeFeedPolicy();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_ChangeFeedPolicy(Microsoft.Azure.Cosmos.ChangeFeedPolicy);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.ChangeFeedPolicy get_ChangeFeedPolicy()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedPolicy get_ChangeFeedPolicy();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_ChangeFeedPolicy(Microsoft.Azure.Cosmos.ChangeFeedPolicy)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void set_ChangeFeedPolicy(Microsoft.Azure.Cosmos.ChangeFeedPolicy);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosClientOptions;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Boolean EnableRemoteRegionPreferredForSessionRetry": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Boolean EnableRemoteRegionPreferredForSessionRetry;CanRead:True;CanWrite:True;Boolean get_EnableRemoteRegionPreferredForSessionRetry();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_EnableRemoteRegionPreferredForSessionRetry(Boolean);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Boolean get_EnableRemoteRegionPreferredForSessionRetry()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean get_EnableRemoteRegionPreferredForSessionRetry();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Int32] get_ThroughputBucket()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[System.Int32] get_ThroughputBucket();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Int32] ThroughputBucket": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Nullable`1[System.Int32] ThroughputBucket;CanRead:True;CanWrite:True;System.Nullable`1[System.Int32] get_ThroughputBucket();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_ThroughputBucket(System.Nullable`1[System.Int32]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_EnableRemoteRegionPreferredForSessionRetry(Boolean)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void set_EnableRemoteRegionPreferredForSessionRetry(Boolean);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_ThroughputBucket(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_ThroughputBucket(System.Nullable`1[System.Int32]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosClientTelemetryOptions;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Boolean get_IsClientMetricsEnabled()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Boolean get_IsClientMetricsEnabled();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Boolean IsClientMetricsEnabled": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Boolean IsClientMetricsEnabled;CanRead:True;CanWrite:True;Boolean get_IsClientMetricsEnabled();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_IsClientMetricsEnabled(Boolean);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.NetworkMetricsOptions get_NetworkMetricsOptions()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.NetworkMetricsOptions get_NetworkMetricsOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.NetworkMetricsOptions NetworkMetricsOptions": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.NetworkMetricsOptions NetworkMetricsOptions;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.NetworkMetricsOptions get_NetworkMetricsOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_NetworkMetricsOptions(Microsoft.Azure.Cosmos.NetworkMetricsOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.OperationMetricsOptions get_OperationMetricsOptions()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.OperationMetricsOptions get_OperationMetricsOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.OperationMetricsOptions OperationMetricsOptions": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.OperationMetricsOptions OperationMetricsOptions;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.OperationMetricsOptions get_OperationMetricsOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_OperationMetricsOptions(Microsoft.Azure.Cosmos.OperationMetricsOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_IsClientMetricsEnabled(Boolean)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_IsClientMetricsEnabled(Boolean);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_NetworkMetricsOptions(Microsoft.Azure.Cosmos.NetworkMetricsOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_NetworkMetricsOptions(Microsoft.Azure.Cosmos.NetworkMetricsOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_OperationMetricsOptions(Microsoft.Azure.Cosmos.OperationMetricsOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_OperationMetricsOptions(Microsoft.Azure.Cosmos.OperationMetricsOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+HistogramBuckets": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + } + }, + "NestedTypes": { + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+HistogramBuckets;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Double[] RequestLatencyBuckets": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Double[] RequestLatencyBuckets;IsInitOnly:True;IsStatic:True;" + }, + "Double[] RequestUnitBuckets": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Double[] RequestUnitBuckets;IsInitOnly:True;IsStatic:True;" + }, + "Double[] RowCountBuckets": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Double[] RowCountBuckets;IsInitOnly:True;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Description": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Name": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Unit": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "System.String MeterName": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String MeterName;IsInitOnly:False;IsStatic:True;" + }, + "System.String Version": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Version;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": { + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Description;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String BackendLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String BackendLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ChannelAquisitionLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ChannelAquisitionLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ReceivedTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ReceivedTimeLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String ResponseBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ResponseBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String TransitTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String TransitTimeLatency;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Name;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String BackendLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String BackendLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ChannelAquisitionLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ChannelAquisitionLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ReceivedTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ReceivedTimeLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String ResponseBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ResponseBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String TransitTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String TransitTimeLatency;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Unit;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String Bytes": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Bytes;IsInitOnly:False;IsStatic:True;" + }, + "System.String Sec": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Sec;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + } + } + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Description": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Name": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Unit": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "System.String MeterName": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String MeterName;IsInitOnly:False;IsStatic:True;" + }, + "System.String Version": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Version;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": { + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Description;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String ActiveInstances": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ActiveInstances;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestCharge": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestCharge;IsInitOnly:False;IsStatic:True;" + }, + "System.String RowCount": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RowCount;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Name;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String ActiveInstances": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ActiveInstances;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestCharge": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestCharge;IsInitOnly:False;IsStatic:True;" + }, + "System.String RowCount": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RowCount;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Unit;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String Instance": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Instance;IsInitOnly:False;IsStatic:True;" + }, + "System.String Item": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Item;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestUnit": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestUnit;IsInitOnly:False;IsStatic:True;" + }, + "System.String Sec": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Sec;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + } + } + } + } + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+HistogramBuckets;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Double[] RequestLatencyBuckets": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Double[] RequestLatencyBuckets;IsInitOnly:True;IsStatic:True;" + }, + "Double[] RequestUnitBuckets": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Double[] RequestUnitBuckets;IsInitOnly:True;IsStatic:True;" + }, + "Double[] RowCountBuckets": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Double[] RowCountBuckets;IsInitOnly:True;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Description": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Name": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Unit": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "System.String MeterName": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String MeterName;IsInitOnly:False;IsStatic:True;" + }, + "System.String Version": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Version;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": { + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Description;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String BackendLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String BackendLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ChannelAquisitionLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ChannelAquisitionLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ReceivedTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ReceivedTimeLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String ResponseBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ResponseBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String TransitTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String TransitTimeLatency;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Name;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String BackendLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String BackendLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ChannelAquisitionLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ChannelAquisitionLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ReceivedTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ReceivedTimeLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String ResponseBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ResponseBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String TransitTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String TransitTimeLatency;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Unit;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String Bytes": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Bytes;IsInitOnly:False;IsStatic:True;" + }, + "System.String Sec": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Sec;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + } + } + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Description;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String BackendLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String BackendLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ChannelAquisitionLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ChannelAquisitionLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ReceivedTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ReceivedTimeLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String ResponseBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ResponseBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String TransitTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String TransitTimeLatency;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Name;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String BackendLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String BackendLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ChannelAquisitionLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ChannelAquisitionLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String ReceivedTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ReceivedTimeLatency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String ResponseBodySize": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ResponseBodySize;IsInitOnly:False;IsStatic:True;" + }, + "System.String TransitTimeLatency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String TransitTimeLatency;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+NetworkMetrics+Unit;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String Bytes": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Bytes;IsInitOnly:False;IsStatic:True;" + }, + "System.String Sec": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Sec;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Description": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Name": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Unit": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null + }, + "System.String MeterName": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String MeterName;IsInitOnly:False;IsStatic:True;" + }, + "System.String Version": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Version;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": { + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Description;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String ActiveInstances": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ActiveInstances;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestCharge": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestCharge;IsInitOnly:False;IsStatic:True;" + }, + "System.String RowCount": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RowCount;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Name;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String ActiveInstances": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ActiveInstances;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestCharge": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestCharge;IsInitOnly:False;IsStatic:True;" + }, + "System.String RowCount": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RowCount;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Unit;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String Instance": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Instance;IsInitOnly:False;IsStatic:True;" + }, + "System.String Item": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Item;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestUnit": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestUnit;IsInitOnly:False;IsStatic:True;" + }, + "System.String Sec": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Sec;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + } + } + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Description;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String ActiveInstances": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ActiveInstances;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestCharge": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestCharge;IsInitOnly:False;IsStatic:True;" + }, + "System.String RowCount": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RowCount;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Name;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String ActiveInstances": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String ActiveInstances;IsInitOnly:False;IsStatic:True;" + }, + "System.String Latency": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Latency;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestCharge": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestCharge;IsInitOnly:False;IsStatic:True;" + }, + "System.String RowCount": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RowCount;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.CosmosDbClientMetrics+OperationMetrics+Unit;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:True;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String Instance": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Instance;IsInitOnly:False;IsStatic:True;" + }, + "System.String Item": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Item;IsInitOnly:False;IsStatic:True;" + }, + "System.String RequestUnit": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RequestUnit;IsInitOnly:False;IsStatic:True;" + }, + "System.String Sec": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Sec;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Fluent.ChangeFeedPolicyDefinition;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Fluent.ContainerBuilder Attach()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.ContainerBuilder Attach();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Fluent.ContainerBuilder;Microsoft.Azure.Cosmos.Fluent.ContainerDefinition`1[[Microsoft.Azure.Cosmos.Fluent.ContainerBuilder, ]];IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Fluent.ChangeFeedPolicyDefinition WithChangeFeedPolicy(System.TimeSpan)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.ChangeFeedPolicyDefinition WithChangeFeedPolicy(System.TimeSpan);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(Boolean)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(Boolean);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithThroughputBucket(Int32)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithThroughputBucket(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:True;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithIndexingSearchListSize(Int32)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithIndexingSearchListSize(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithQuantizationByteSize(Int32)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithQuantizationByteSize(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithQuantizerType(Microsoft.Azure.Cosmos.QuantizerType)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithQuantizerType(Microsoft.Azure.Cosmos.QuantizerType);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithVectorIndexShardKey(System.String[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithVectorIndexShardKey(System.String[]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.QueryDefinition Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ToQueryDefinition[T](System.Linq.IQueryable`1[T], System.Collections.Generic.IDictionary`2[System.Object,System.String])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryDefinition Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ToQueryDefinition[T](System.Linq.IQueryable`1[T], System.Collections.Generic.IDictionary`2[System.Object,System.String]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.NetworkMetricsOptions;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Collections.Generic.IDictionary`2[System.String,System.String] CustomDimensions": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Collections.Generic.IDictionary`2[System.String,System.String] CustomDimensions;CanRead:True;CanWrite:True;System.Collections.Generic.IDictionary`2[System.String,System.String] get_CustomDimensions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_CustomDimensions(System.Collections.Generic.IDictionary`2[System.String,System.String]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.IDictionary`2[System.String,System.String] get_CustomDimensions()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Collections.Generic.IDictionary`2[System.String,System.String] get_CustomDimensions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Boolean] get_IncludeRoutingId()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[System.Boolean] get_IncludeRoutingId();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Boolean] IncludeRoutingId": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Nullable`1[System.Boolean] IncludeRoutingId;CanRead:True;CanWrite:True;System.Nullable`1[System.Boolean] get_IncludeRoutingId();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_IncludeRoutingId(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + }, + "Void set_CustomDimensions(System.Collections.Generic.IDictionary`2[System.String,System.String])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_CustomDimensions(System.Collections.Generic.IDictionary`2[System.String,System.String]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_IncludeRoutingId(System.Nullable`1[System.Boolean])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_IncludeRoutingId(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.OperationMetricsOptions;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Collections.Generic.IDictionary`2[System.String,System.String] CustomDimensions": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Collections.Generic.IDictionary`2[System.String,System.String] CustomDimensions;CanRead:True;CanWrite:True;System.Collections.Generic.IDictionary`2[System.String,System.String] get_CustomDimensions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_CustomDimensions(System.Collections.Generic.IDictionary`2[System.String,System.String]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.IDictionary`2[System.String,System.String] get_CustomDimensions()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Collections.Generic.IDictionary`2[System.String,System.String] get_CustomDimensions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Boolean] get_IncludeRegion()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[System.Boolean] get_IncludeRegion();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Boolean] IncludeRegion": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Nullable`1[System.Boolean] IncludeRegion;CanRead:True;CanWrite:True;System.Nullable`1[System.Boolean] get_IncludeRegion();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_IncludeRegion(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + }, + "Void set_CustomDimensions(System.Collections.Generic.IDictionary`2[System.String,System.String])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_CustomDimensions(System.Collections.Generic.IDictionary`2[System.String,System.String]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_IncludeRegion(System.Nullable`1[System.Boolean])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_IncludeRegion(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.QuantizerType;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Int32 value__": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Int32 value__;IsInitOnly:False;IsStatic:False;" + }, + "Microsoft.Azure.Cosmos.QuantizerType Product[System.Runtime.Serialization.EnumMemberAttribute(Value = \"product\")]": { + "Type": "Field", + "Attributes": [ + "EnumMemberAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.QuantizerType Product;IsInitOnly:False;IsStatic:True;" + }, + "Microsoft.Azure.Cosmos.QuantizerType Spherical[System.Runtime.Serialization.EnumMemberAttribute(Value = \"spherical\")]": { + "Type": "Field", + "Attributes": [ + "EnumMemberAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.QuantizerType Spherical;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.RequestOptions;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Microsoft.Azure.Cosmos.NetworkMetricsOptions get_NetworkMetricsOptions()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.NetworkMetricsOptions get_NetworkMetricsOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.NetworkMetricsOptions NetworkMetricsOptions": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.NetworkMetricsOptions NetworkMetricsOptions;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.NetworkMetricsOptions get_NetworkMetricsOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_NetworkMetricsOptions(Microsoft.Azure.Cosmos.NetworkMetricsOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.OperationMetricsOptions get_OperationMetricsOptions()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.OperationMetricsOptions get_OperationMetricsOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.OperationMetricsOptions OperationMetricsOptions": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.OperationMetricsOptions OperationMetricsOptions;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.OperationMetricsOptions get_OperationMetricsOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_OperationMetricsOptions(Microsoft.Azure.Cosmos.OperationMetricsOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Int32] get_ThroughputBucket()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[System.Int32] get_ThroughputBucket();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Int32] ThroughputBucket": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Nullable`1[System.Int32] ThroughputBucket;CanRead:True;CanWrite:True;System.Nullable`1[System.Int32] get_ThroughputBucket();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_ThroughputBucket(System.Nullable`1[System.Int32]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_NetworkMetricsOptions(Microsoft.Azure.Cosmos.NetworkMetricsOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_NetworkMetricsOptions(Microsoft.Azure.Cosmos.NetworkMetricsOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_OperationMetricsOptions(Microsoft.Azure.Cosmos.OperationMetricsOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_OperationMetricsOptions(Microsoft.Azure.Cosmos.OperationMetricsOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_ThroughputBucket(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_ThroughputBucket(System.Nullable`1[System.Int32]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.RerankScore;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Double get_Score()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Double get_Score();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Double Score": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Double Score;CanRead:True;CanWrite:False;Double get_Score();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 get_Index()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Int32 get_Index();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 Index": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Int32 Index;CanRead:True;CanWrite:False;Int32 get_Index();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String Document": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String Document;CanRead:True;CanWrite:False;System.String get_Document();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_Document()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_Document();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(System.String, Double, Int32)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.String, Double, Int32)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.SemanticRerankResult;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.Collections.Generic.Dictionary`2[System.String,System.Object] get_Latency()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Collections.Generic.Dictionary`2[System.String,System.Object] get_Latency();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.Dictionary`2[System.String,System.Object] get_TokenUseage()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Collections.Generic.Dictionary`2[System.String,System.Object] get_TokenUseage();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.Dictionary`2[System.String,System.Object] Latency": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Collections.Generic.Dictionary`2[System.String,System.Object] Latency;CanRead:True;CanWrite:False;System.Collections.Generic.Dictionary`2[System.String,System.Object] get_Latency();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.Dictionary`2[System.String,System.Object] TokenUseage": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Collections.Generic.Dictionary`2[System.String,System.Object] TokenUseage;CanRead:True;CanWrite:False;System.Collections.Generic.Dictionary`2[System.String,System.Object] get_TokenUseage();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.RerankScore] get_RerankScores()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.RerankScore] get_RerankScores();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.RerankScore] RerankScores": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.RerankScore] RerankScores;CanRead:True;CanWrite:False;System.Collections.Generic.IReadOnlyList`1[Microsoft.Azure.Cosmos.RerankScore] get_RerankScores();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Net.Http.Headers.HttpResponseHeaders get_Headers()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Net.Http.Headers.HttpResponseHeaders get_Headers();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Net.Http.Headers.HttpResponseHeaders Headers": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Net.Http.Headers.HttpResponseHeaders Headers;CanRead:True;CanWrite:False;System.Net.Http.Headers.HttpResponseHeaders get_Headers();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.VectorIndexPath;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "Int32 get_QuantizationByteSize()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 get_QuantizationByteSize();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 QuantizationByteSize[Newtonsoft.Json.JsonIgnoreAttribute()]": { + "Type": "Property", + "Attributes": [ + "JsonIgnoreAttribute" + ], + "MethodInfo": "Int32 QuantizationByteSize;CanRead:True;CanWrite:True;Int32 get_QuantizationByteSize();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_QuantizationByteSize(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType] get_QuantizerType()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType] get_QuantizerType();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType] QuantizerType[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"quantizerType\")]-[Newtonsoft.Json.JsonConverterAttribute(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]": { + "Type": "Property", + "Attributes": [ + "JsonConverterAttribute", + "JsonPropertyAttribute" + ], + "MethodInfo": "System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType] QuantizerType;CanRead:True;CanWrite:True;System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType] get_QuantizerType();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_QuantizerType(System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_QuantizationByteSize(Int32)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void set_QuantizationByteSize(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_QuantizerType(System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_QuantizerType(System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + } + }, + "Members": {}, + "NestedTypes": {} +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.net6.json similarity index 93% rename from Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json rename to Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.net6.json index 54e00c2a53..63865a40ad 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.net6.json @@ -49,7 +49,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} @@ -95,7 +95,7 @@ ], "MethodInfo": "System.Collections.Generic.IEnumerable`1[Microsoft.Azure.Cosmos.AccountRegion] WritableRegions;CanRead:True;CanWrite:False;System.Collections.Generic.IEnumerable`1[Microsoft.Azure.Cosmos.AccountRegion] get_WritableRegions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -158,7 +158,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} @@ -166,15 +166,15 @@ "Microsoft.Azure.Cosmos.AvailabilityStrategy;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "Microsoft.Azure.Cosmos.AvailabilityStrategy CrossRegionHedgingStrategy(System.TimeSpan, System.Nullable`1[System.TimeSpan], Boolean)": { + "Microsoft.Azure.Cosmos.AvailabilityStrategy Microsoft.Azure.Cosmos.AvailabilityStrategy.CrossRegionHedgingStrategy(System.TimeSpan, System.Nullable`1[System.TimeSpan], System.Boolean)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy CrossRegionHedgingStrategy(System.TimeSpan, System.Nullable`1[System.TimeSpan], Boolean);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy Microsoft.Azure.Cosmos.AvailabilityStrategy.CrossRegionHedgingStrategy(System.TimeSpan, System.Nullable`1[System.TimeSpan], System.Boolean);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.AvailabilityStrategy DisabledStrategy()": { + "Microsoft.Azure.Cosmos.AvailabilityStrategy Microsoft.Azure.Cosmos.AvailabilityStrategy.DisabledStrategy()": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy DisabledStrategy();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy Microsoft.Azure.Cosmos.AvailabilityStrategy.DisabledStrategy();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -241,7 +241,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Xmax(Double)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -303,7 +303,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_MaxItemCount(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -318,25 +318,25 @@ "Microsoft.Azure.Cosmos.ChangeFeedMode;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "Microsoft.Azure.Cosmos.ChangeFeedMode get_Incremental()": { - "Type": "Method", - "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMode get_Incremental();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Microsoft.Azure.Cosmos.ChangeFeedMode get_LatestVersion()": { - "Type": "Method", - "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMode get_LatestVersion();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, "Microsoft.Azure.Cosmos.ChangeFeedMode Incremental": { "Type": "Property", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMode Incremental;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.ChangeFeedMode get_Incremental();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMode Incremental;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.ChangeFeedMode Microsoft.Azure.Cosmos.ChangeFeedMode.get_Incremental();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.ChangeFeedMode LatestVersion": { "Type": "Property", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMode LatestVersion;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.ChangeFeedMode get_LatestVersion();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMode LatestVersion;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.ChangeFeedMode Microsoft.Azure.Cosmos.ChangeFeedMode.get_LatestVersion();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.ChangeFeedMode Microsoft.Azure.Cosmos.ChangeFeedMode.get_Incremental()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMode Microsoft.Azure.Cosmos.ChangeFeedMode.get_Incremental();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.ChangeFeedMode Microsoft.Azure.Cosmos.ChangeFeedMode.get_LatestVersion()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedMode Microsoft.Azure.Cosmos.ChangeFeedMode.get_LatestVersion();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -506,7 +506,7 @@ "Void .ctor(System.String, Int64, System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, Int64, System.String), Void .ctor(System.String, Int64, System.String)]" + "MethodInfo": "Void .ctor(System.String, Int64, System.String)" } }, "NestedTypes": {} @@ -529,7 +529,7 @@ "Void .ctor(System.Exception, Microsoft.Azure.Cosmos.ChangeFeedProcessorContext)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Exception, Microsoft.Azure.Cosmos.ChangeFeedProcessorContext), Void .ctor(System.Exception, Microsoft.Azure.Cosmos.ChangeFeedProcessorContext)]" + "MethodInfo": "Void .ctor(System.Exception, Microsoft.Azure.Cosmos.ChangeFeedProcessorContext)" }, "Void GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)": { "Type": "Method", @@ -562,7 +562,7 @@ "Attributes": [], "MethodInfo": "System.String get_IfNoneMatchEtag();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String IfMatchEtag[System.ObsoleteAttribute(\"IfMatchEtag is inherited from the base class but not used.\")]-[System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)1)]": { + "System.String IfMatchEtag[System.ObsoleteAttribute(\"IfMatchEtag is inherited from the base class but not used.\")]-[System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never = 1)]": { "Type": "Property", "Attributes": [ "EditorBrowsableAttribute", @@ -570,7 +570,7 @@ ], "MethodInfo": "System.String IfMatchEtag;CanRead:True;CanWrite:True;System.String get_IfMatchEtag();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_IfMatchEtag(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String IfNoneMatchEtag[System.ObsoleteAttribute(\"IfNoneMatchEtag is inherited from the base class but not used.\")]-[System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)1)]": { + "System.String IfNoneMatchEtag[System.ObsoleteAttribute(\"IfNoneMatchEtag is inherited from the base class but not used.\")]-[System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never = 1)]": { "Type": "Property", "Attributes": [ "EditorBrowsableAttribute", @@ -581,7 +581,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_IfMatchEtag(System.String)": { "Type": "Method", @@ -604,40 +604,40 @@ "Microsoft.Azure.Cosmos.ChangeFeedStartFrom;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Beginning()": { + "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Beginning()": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Beginning();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Beginning();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Beginning(Microsoft.Azure.Cosmos.FeedRange)": { + "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Beginning(Microsoft.Azure.Cosmos.FeedRange)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Beginning(Microsoft.Azure.Cosmos.FeedRange);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Beginning(Microsoft.Azure.Cosmos.FeedRange);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.ChangeFeedStartFrom ContinuationToken(System.String)": { + "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.ContinuationToken(System.String)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom ContinuationToken(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.ContinuationToken(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Now()": { + "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Now()": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Now();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Now();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Now(Microsoft.Azure.Cosmos.FeedRange)": { + "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Now(Microsoft.Azure.Cosmos.FeedRange)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Now(Microsoft.Azure.Cosmos.FeedRange);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Now(Microsoft.Azure.Cosmos.FeedRange);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Time(System.DateTime, Microsoft.Azure.Cosmos.FeedRange)": { + "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Time(System.DateTime, Microsoft.Azure.Cosmos.FeedRange)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Time(System.DateTime, Microsoft.Azure.Cosmos.FeedRange);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Time(System.DateTime, Microsoft.Azure.Cosmos.FeedRange);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Time(System.DateTime)": { + "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Time(System.DateTime)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Time(System.DateTime);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ChangeFeedStartFrom Microsoft.Azure.Cosmos.ChangeFeedStartFrom.Time(System.DateTime);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -704,7 +704,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ClientEncryptionKeyId(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -783,7 +783,7 @@ ], "MethodInfo": "Byte[] get_WrappedDataEncryptionKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Byte[] WrappedDataEncryptionKey[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"wrappedDataEncryptionKey\")]": { + "Byte[] WrappedDataEncryptionKey[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"wrappedDataEncryptionKey\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -795,7 +795,7 @@ "Attributes": [], "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"keyWrapMetadata\")]": { + "Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"keyWrapMetadata\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -809,7 +809,7 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata get_EncryptionKeyWrapMetadata();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.DateTime] CreatedTime[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_cts\")]": { + "System.Nullable`1[System.DateTime] CreatedTime[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_cts\")]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -831,7 +831,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_ts\")]": { + "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_ts\")]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -839,14 +839,14 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] LastModified;CanRead:True;CanWrite:True;System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String EncryptionAlgorithm[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"encryptionAlgorithm\")]": { + "System.String EncryptionAlgorithm[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"encryptionAlgorithm\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" ], "MethodInfo": "System.String EncryptionAlgorithm;CanRead:True;CanWrite:True;System.String get_EncryptionAlgorithm();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -888,7 +888,7 @@ ], "MethodInfo": "System.String Id;CanRead:True;CanWrite:True;System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -898,7 +898,7 @@ "Void .ctor(System.String, System.String, Byte[], Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.String, Byte[], Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata), Void .ctor(System.String, System.String, Byte[], Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata)]" + "MethodInfo": "Void .ctor(System.String, System.String, Byte[], Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata)" } }, "NestedTypes": {} @@ -928,10 +928,10 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.ClientEncryptionKey get_ClientEncryptionKey();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.ClientEncryptionKey op_Implicit(Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse)": { + "Microsoft.Azure.Cosmos.ClientEncryptionKey Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse.op_Implicit(Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ClientEncryptionKey op_Implicit(Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ClientEncryptionKey Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse.op_Implicit(Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.ClientEncryptionKeyProperties get_Resource()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -1038,12 +1038,12 @@ "Void .ctor(System.Collections.Generic.IEnumerable`1[Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath], Int32)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IEnumerable`1[Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath], Int32), Void .ctor(System.Collections.Generic.IEnumerable`1[Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath], Int32)]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IEnumerable`1[Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath], Int32)" }, "Void .ctor(System.Collections.Generic.IEnumerable`1[Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IEnumerable`1[Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath]), Void .ctor(System.Collections.Generic.IEnumerable`1[Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IEnumerable`1[Microsoft.Azure.Cosmos.ClientEncryptionIncludedPath])" } }, "NestedTypes": {} @@ -1083,7 +1083,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Order(Microsoft.Azure.Cosmos.CompositePathSortOrder)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -1161,7 +1161,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Name(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -1219,7 +1219,7 @@ ], "MethodInfo": "System.String Id;CanRead:True;CanWrite:True;System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -1229,7 +1229,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} @@ -1265,7 +1265,7 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.ConflictResolutionMode get_Mode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.ConflictResolutionMode Mode[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"mode\")]-[Newtonsoft.Json.JsonConverterAttribute(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]": { + "Microsoft.Azure.Cosmos.ConflictResolutionMode Mode[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"mode\")]-[Newtonsoft.Json.JsonConverterAttribute(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -1287,14 +1287,14 @@ ], "MethodInfo": "System.String get_ResolutionProcedure();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ResolutionPath[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"conflictResolutionPath\")]": { + "System.String ResolutionPath[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"conflictResolutionPath\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" ], "MethodInfo": "System.String ResolutionPath;CanRead:True;CanWrite:True;System.String get_ResolutionPath();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_ResolutionPath(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ResolutionProcedure[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"conflictResolutionProcedure\")]": { + "System.String ResolutionProcedure[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"conflictResolutionProcedure\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -1304,7 +1304,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Mode(Microsoft.Azure.Cosmos.ConflictResolutionMode)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -1754,7 +1754,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -1780,7 +1780,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -1806,7 +1806,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -1832,7 +1832,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -1858,7 +1858,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -1884,7 +1884,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -1910,7 +1910,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -1936,7 +1936,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -1962,7 +1962,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -1990,7 +1990,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -2016,7 +2016,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -2042,7 +2042,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -2068,7 +2068,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -2094,7 +2094,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -2120,7 +2120,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -2146,7 +2146,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -2172,7 +2172,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -2198,7 +2198,7 @@ "Void .ctor(System.Object, IntPtr)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Object, IntPtr), Void .ctor(System.Object, IntPtr)]" + "MethodInfo": "Void .ctor(System.Object, IntPtr)" } }, "NestedTypes": {} @@ -2333,7 +2333,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_ts\")]-[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]": { + "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_ts\")]-[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -2341,14 +2341,14 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] LastModified;CanRead:True;CanWrite:True;System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.Int32] AnalyticalStoreTimeToLiveInSeconds[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"analyticalStorageTtl\")]": { + "System.Nullable`1[System.Int32] AnalyticalStoreTimeToLiveInSeconds[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"analyticalStorageTtl\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" ], "MethodInfo": "System.Nullable`1[System.Int32] AnalyticalStoreTimeToLiveInSeconds;CanRead:True;CanWrite:True;System.Nullable`1[System.Int32] get_AnalyticalStoreTimeToLiveInSeconds();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_AnalyticalStoreTimeToLiveInSeconds(System.Nullable`1[System.Int32]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.Int32] DefaultTimeToLive[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"defaultTtl\")]": { + "System.Nullable`1[System.Int32] DefaultTimeToLive[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"defaultTtl\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -2369,7 +2369,7 @@ ], "MethodInfo": "System.Nullable`1[System.Int32] get_DefaultTimeToLive();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -2421,14 +2421,14 @@ ], "MethodInfo": "System.String PartitionKeyPath;CanRead:True;CanWrite:True;System.String get_PartitionKeyPath();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_PartitionKeyPath(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" ], "MethodInfo": "System.String SelfLink;CanRead:True;CanWrite:True;System.String get_SelfLink();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String TimeToLivePropertyPath[System.ObsoleteAttribute()]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"ttlPropertyPath\")]": { + "System.String TimeToLivePropertyPath[System.ObsoleteAttribute()]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"ttlPropertyPath\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute", @@ -2439,17 +2439,17 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void .ctor(System.String, System.Collections.Generic.IReadOnlyList`1[System.String])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.Collections.Generic.IReadOnlyList`1[System.String]), Void .ctor(System.String, System.Collections.Generic.IReadOnlyList`1[System.String])]" + "MethodInfo": "Void .ctor(System.String, System.Collections.Generic.IReadOnlyList`1[System.String])" }, "Void .ctor(System.String, System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.String), Void .ctor(System.String, System.String)]" + "MethodInfo": "Void .ctor(System.String, System.String)" }, "Void set_AnalyticalStoreTimeToLiveInSeconds(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -2553,7 +2553,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_PopulateQuotaInfo(Boolean)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -2590,10 +2590,10 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Container get_Container();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Container op_Implicit(Microsoft.Azure.Cosmos.ContainerResponse)": { + "Microsoft.Azure.Cosmos.Container Microsoft.Azure.Cosmos.ContainerResponse.op_Implicit(Microsoft.Azure.Cosmos.ContainerResponse)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Container op_Implicit(Microsoft.Azure.Cosmos.ContainerResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Container Microsoft.Azure.Cosmos.ContainerResponse.op_Implicit(Microsoft.Azure.Cosmos.ContainerResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.ContainerProperties get_Resource()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -2724,33 +2724,33 @@ "Attributes": [], "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.AccountProperties] ReadAccountAsync();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] CreateAndInitializeAsync(System.String, Azure.AzureKeyCredential, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.CosmosClient+))]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] Microsoft.Azure.Cosmos.CosmosClient.CreateAndInitializeAsync(System.String, Azure.AzureKeyCredential, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.CosmosClient+))]": { "Type": "Method", "Attributes": [ "AsyncStateMachineAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] CreateAndInitializeAsync(System.String, Azure.AzureKeyCredential, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] Microsoft.Azure.Cosmos.CosmosClient.CreateAndInitializeAsync(System.String, Azure.AzureKeyCredential, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] CreateAndInitializeAsync(System.String, Azure.Core.TokenCredential, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.CosmosClient+))]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] Microsoft.Azure.Cosmos.CosmosClient.CreateAndInitializeAsync(System.String, Azure.Core.TokenCredential, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.CosmosClient+))]": { "Type": "Method", "Attributes": [ "AsyncStateMachineAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] CreateAndInitializeAsync(System.String, Azure.Core.TokenCredential, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] Microsoft.Azure.Cosmos.CosmosClient.CreateAndInitializeAsync(System.String, Azure.Core.TokenCredential, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] CreateAndInitializeAsync(System.String, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.CosmosClient+))]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] Microsoft.Azure.Cosmos.CosmosClient.CreateAndInitializeAsync(System.String, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.CosmosClient+))]": { "Type": "Method", "Attributes": [ "AsyncStateMachineAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] CreateAndInitializeAsync(System.String, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] Microsoft.Azure.Cosmos.CosmosClient.CreateAndInitializeAsync(System.String, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] CreateAndInitializeAsync(System.String, System.String, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.CosmosClient+))]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] Microsoft.Azure.Cosmos.CosmosClient.CreateAndInitializeAsync(System.String, System.String, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.CosmosClient+))]": { "Type": "Method", "Attributes": [ "AsyncStateMachineAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] CreateAndInitializeAsync(System.String, System.String, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosClient] Microsoft.Azure.Cosmos.CosmosClient.CreateAndInitializeAsync(System.String, System.String, System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.String]], Microsoft.Azure.Cosmos.CosmosClientOptions, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.DatabaseResponse] CreateDatabaseAsync(System.String, Microsoft.Azure.Cosmos.ThroughputProperties, Microsoft.Azure.Cosmos.RequestOptions, System.Threading.CancellationToken)": { "Type": "Method", @@ -2792,22 +2792,22 @@ "Void .ctor(System.String, Azure.AzureKeyCredential, Microsoft.Azure.Cosmos.CosmosClientOptions)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, Azure.AzureKeyCredential, Microsoft.Azure.Cosmos.CosmosClientOptions), Void .ctor(System.String, Azure.AzureKeyCredential, Microsoft.Azure.Cosmos.CosmosClientOptions)]" + "MethodInfo": "Void .ctor(System.String, Azure.AzureKeyCredential, Microsoft.Azure.Cosmos.CosmosClientOptions)" }, "Void .ctor(System.String, Azure.Core.TokenCredential, Microsoft.Azure.Cosmos.CosmosClientOptions)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, Azure.Core.TokenCredential, Microsoft.Azure.Cosmos.CosmosClientOptions), Void .ctor(System.String, Azure.Core.TokenCredential, Microsoft.Azure.Cosmos.CosmosClientOptions)]" + "MethodInfo": "Void .ctor(System.String, Azure.Core.TokenCredential, Microsoft.Azure.Cosmos.CosmosClientOptions)" }, "Void .ctor(System.String, Microsoft.Azure.Cosmos.CosmosClientOptions)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, Microsoft.Azure.Cosmos.CosmosClientOptions), Void .ctor(System.String, Microsoft.Azure.Cosmos.CosmosClientOptions)]" + "MethodInfo": "Void .ctor(System.String, Microsoft.Azure.Cosmos.CosmosClientOptions)" }, "Void .ctor(System.String, System.String, Microsoft.Azure.Cosmos.CosmosClientOptions)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.String, Microsoft.Azure.Cosmos.CosmosClientOptions), Void .ctor(System.String, System.String, Microsoft.Azure.Cosmos.CosmosClientOptions)]" + "MethodInfo": "Void .ctor(System.String, System.String, Microsoft.Azure.Cosmos.CosmosClientOptions)" }, "Void Dispose()": { "Type": "Method", @@ -3175,7 +3175,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_AccountInitializationCustomEndpoints(System.Collections.Generic.IEnumerable`1[System.Uri])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -3416,7 +3416,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_CosmosThresholdOptions(Microsoft.Azure.Cosmos.CosmosThresholdOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -3614,7 +3614,7 @@ "Void .ctor(System.String, System.Net.HttpStatusCode, Int32, System.String, Double)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.Net.HttpStatusCode, Int32, System.String, Double), Void .ctor(System.String, System.Net.HttpStatusCode, Int32, System.String, Double)]" + "MethodInfo": "Void .ctor(System.String, System.Net.HttpStatusCode, Int32, System.String, Double)" } }, "NestedTypes": {} @@ -3648,7 +3648,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_PropertyNamingPolicy(Microsoft.Azure.Cosmos.CosmosPropertyNamingPolicy)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -3743,7 +3743,7 @@ "Void .ctor(System.OperationCanceledException, Microsoft.Azure.Cosmos.CosmosDiagnostics)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.OperationCanceledException, Microsoft.Azure.Cosmos.CosmosDiagnostics), Void .ctor(System.OperationCanceledException, Microsoft.Azure.Cosmos.CosmosDiagnostics)]" + "MethodInfo": "Void .ctor(System.OperationCanceledException, Microsoft.Azure.Cosmos.CosmosDiagnostics)" }, "Void GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)": { "Type": "Method", @@ -3847,7 +3847,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_IgnoreNullValues(Boolean)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -3955,7 +3955,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_NonPointOperationLatencyThreshold(System.TimeSpan)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -4174,7 +4174,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_ts\")]": { + "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_ts\")]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -4182,7 +4182,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] LastModified;CanRead:True;CanWrite:True;System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -4215,7 +4215,7 @@ ], "MethodInfo": "System.String Id;CanRead:True;CanWrite:True;System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Id(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -4225,12 +4225,12 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void .ctor(System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String), Void .ctor(System.String)]" + "MethodInfo": "Void .ctor(System.String)" }, "Void set_Id(System.String)": { "Type": "Method", @@ -4277,10 +4277,10 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Database get_Database();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Database op_Implicit(Microsoft.Azure.Cosmos.DatabaseResponse)": { + "Microsoft.Azure.Cosmos.Database Microsoft.Azure.Cosmos.DatabaseResponse.op_Implicit(Microsoft.Azure.Cosmos.DatabaseResponse)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Database op_Implicit(Microsoft.Azure.Cosmos.DatabaseResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Database Microsoft.Azure.Cosmos.DatabaseResponse.op_Implicit(Microsoft.Azure.Cosmos.DatabaseResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.DatabaseProperties get_Resource()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -4412,7 +4412,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_BypassIntegratedCache(System.Nullable`1[System.Boolean])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -4532,7 +4532,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_DataType(Microsoft.Azure.Cosmos.VectorDataType)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -4588,7 +4588,7 @@ "Attributes": [], "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String Algorithm[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"algorithm\")]": { + "System.String Algorithm[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"algorithm\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -4623,21 +4623,21 @@ ], "MethodInfo": "System.String get_Value();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String Name[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"name\")]": { + "System.String Name[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"name\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" ], "MethodInfo": "System.String Name;CanRead:True;CanWrite:True;System.String get_Name();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String Type[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"type\")]": { + "System.String Type[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"type\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" ], "MethodInfo": "System.String Type;CanRead:True;CanWrite:True;System.String get_Type();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String Value[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"value\")]": { + "System.String Value[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"value\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -4647,12 +4647,12 @@ "Void .ctor(Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata), Void .ctor(Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata)" }, "Void .ctor(System.String, System.String, System.String, System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.String, System.String, System.String), Void .ctor(System.String, System.String, System.String, System.String)]" + "MethodInfo": "Void .ctor(System.String, System.String, System.String, System.String)" } }, "NestedTypes": {} @@ -4677,7 +4677,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Path(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -4749,15 +4749,15 @@ "Microsoft.Azure.Cosmos.FeedRange;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:True": { "Subclasses": {}, "Members": { - "Microsoft.Azure.Cosmos.FeedRange FromJsonString(System.String)": { + "Microsoft.Azure.Cosmos.FeedRange Microsoft.Azure.Cosmos.FeedRange.FromJsonString(System.String)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.FeedRange FromJsonString(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.FeedRange Microsoft.Azure.Cosmos.FeedRange.FromJsonString(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.FeedRange FromPartitionKey(Microsoft.Azure.Cosmos.PartitionKey)": { + "Microsoft.Azure.Cosmos.FeedRange Microsoft.Azure.Cosmos.FeedRange.FromPartitionKey(Microsoft.Azure.Cosmos.PartitionKey)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.FeedRange FromPartitionKey(Microsoft.Azure.Cosmos.PartitionKey);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.FeedRange Microsoft.Azure.Cosmos.FeedRange.FromPartitionKey(Microsoft.Azure.Cosmos.PartitionKey);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "System.String ToJsonString()": { "Type": "Method", @@ -4991,7 +4991,7 @@ "Void .ctor(Microsoft.Azure.Cosmos.Database, System.String, System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Database, System.String, System.String), Void .ctor(Microsoft.Azure.Cosmos.Database, System.String, System.String)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Database, System.String, System.String)" } }, "NestedTypes": {} @@ -5037,7 +5037,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} @@ -5168,22 +5168,22 @@ "Void .ctor(System.String, Azure.AzureKeyCredential)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, Azure.AzureKeyCredential), Void .ctor(System.String, Azure.AzureKeyCredential)]" + "MethodInfo": "Void .ctor(System.String, Azure.AzureKeyCredential)" }, "Void .ctor(System.String, Azure.Core.TokenCredential)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, Azure.Core.TokenCredential), Void .ctor(System.String, Azure.Core.TokenCredential)]" + "MethodInfo": "Void .ctor(System.String, Azure.Core.TokenCredential)" }, "Void .ctor(System.String, System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.String), Void .ctor(System.String, System.String)]" + "MethodInfo": "Void .ctor(System.String, System.String)" }, "Void .ctor(System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String), Void .ctor(System.String)]" + "MethodInfo": "Void .ctor(System.String)" } }, "NestedTypes": {} @@ -5204,7 +5204,7 @@ "Void .ctor(T, System.Action`1[Microsoft.Azure.Cosmos.FullTextIndexPath])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(T, System.Action`1[Microsoft.Azure.Cosmos.FullTextIndexPath]), Void .ctor(T, System.Action`1[Microsoft.Azure.Cosmos.FullTextIndexPath])]" + "MethodInfo": "Void .ctor(T, System.Action`1[Microsoft.Azure.Cosmos.FullTextIndexPath])" } }, "NestedTypes": {} @@ -5220,7 +5220,7 @@ "Void .ctor(Microsoft.Azure.Cosmos.Fluent.ContainerBuilder, System.String, System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.FullTextPath], System.Action`1[Microsoft.Azure.Cosmos.FullTextPolicy])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Fluent.ContainerBuilder, System.String, System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.FullTextPath], System.Action`1[Microsoft.Azure.Cosmos.FullTextPolicy]), Void .ctor(Microsoft.Azure.Cosmos.Fluent.ContainerBuilder, System.String, System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.FullTextPath], System.Action`1[Microsoft.Azure.Cosmos.FullTextPolicy])]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Fluent.ContainerBuilder, System.String, System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.FullTextPath], System.Action`1[Microsoft.Azure.Cosmos.FullTextPolicy])" } }, "NestedTypes": {} @@ -5276,7 +5276,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} @@ -5345,7 +5345,7 @@ "Void .ctor(Microsoft.Azure.Cosmos.Fluent.ContainerBuilder, System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.Embedding], System.Action`1[Microsoft.Azure.Cosmos.VectorEmbeddingPolicy])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Fluent.ContainerBuilder, System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.Embedding], System.Action`1[Microsoft.Azure.Cosmos.VectorEmbeddingPolicy]), Void .ctor(Microsoft.Azure.Cosmos.Fluent.ContainerBuilder, System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.Embedding], System.Action`1[Microsoft.Azure.Cosmos.VectorEmbeddingPolicy])]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Fluent.ContainerBuilder, System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.Embedding], System.Action`1[Microsoft.Azure.Cosmos.VectorEmbeddingPolicy])" } }, "NestedTypes": {} @@ -5366,7 +5366,7 @@ "Void .ctor(T, System.Action`1[Microsoft.Azure.Cosmos.VectorIndexPath])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(T, System.Action`1[Microsoft.Azure.Cosmos.VectorIndexPath]), Void .ctor(T, System.Action`1[Microsoft.Azure.Cosmos.VectorIndexPath])]" + "MethodInfo": "Void .ctor(T, System.Action`1[Microsoft.Azure.Cosmos.VectorIndexPath])" } }, "NestedTypes": {} @@ -5391,7 +5391,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Path(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -5425,7 +5425,7 @@ ], "MethodInfo": "System.String get_Path();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String Language[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"language\")]": { + "System.String Language[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"language\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -5442,7 +5442,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Language(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -5481,7 +5481,7 @@ "Attributes": [], "MethodInfo": "System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.FullTextPath] get_FullTextPaths();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String DefaultLanguage[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"defaultLanguage\")]": { + "System.String DefaultLanguage[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"defaultLanguage\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -5498,7 +5498,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_DefaultLanguage(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -5515,6 +5515,27 @@ }, "NestedTypes": {} }, + "Microsoft.Azure.Cosmos.FullTextScoreScope;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Int32 value__": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Int32 value__;IsInitOnly:False;IsStatic:False;" + }, + "Microsoft.Azure.Cosmos.FullTextScoreScope Global": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.FullTextScoreScope Global;IsInitOnly:False;IsStatic:True;" + }, + "Microsoft.Azure.Cosmos.FullTextScoreScope Local": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.FullTextScoreScope Local;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, "Microsoft.Azure.Cosmos.GeospatialConfig;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { @@ -5536,12 +5557,12 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void .ctor(Microsoft.Azure.Cosmos.GeospatialType)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.GeospatialType), Void .ctor(Microsoft.Azure.Cosmos.GeospatialType)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.GeospatialType)" }, "Void set_GeospatialType(Microsoft.Azure.Cosmos.GeospatialType)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -5702,7 +5723,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void Add(System.String, System.Collections.Generic.IEnumerable`1[System.String])": { "Type": "Method", @@ -5757,7 +5778,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Path(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -5867,7 +5888,7 @@ ], "MethodInfo": "System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.ExcludedPath] get_ExcludedPaths();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.FullTextIndexPath] FullTextIndexes[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"fullTextIndexes\")]": { + "System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.FullTextIndexPath] FullTextIndexes[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"fullTextIndexes\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -5916,7 +5937,7 @@ ], "MethodInfo": "System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.VectorIndexPath] get_VectorIndexes();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.VectorIndexPath] VectorIndexes[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"vectorIndexes\")]": { + "System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.VectorIndexPath] VectorIndexes[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"vectorIndexes\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -5940,7 +5961,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Automatic(Boolean)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -6019,7 +6040,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_FilterPredicate(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -6118,7 +6139,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ConsistencyLevel(System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel])": { "Type": "Method", @@ -6257,10 +6278,10 @@ "Microsoft.Azure.Cosmos.Linq.CosmosLinq;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "System.Object InvokeUserDefinedFunction(System.String, System.Object[])": { + "System.Object Microsoft.Azure.Cosmos.Linq.CosmosLinq.InvokeUserDefinedFunction(System.String, System.Object[])": { "Type": "Method", "Attributes": [], - "MethodInfo": "System.Object InvokeUserDefinedFunction(System.String, System.Object[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Object Microsoft.Azure.Cosmos.Linq.CosmosLinq.InvokeUserDefinedFunction(System.String, System.Object[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -6268,330 +6289,356 @@ "Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "Boolean FullTextContains(System.Object, System.String)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Microsoft.Azure.Cosmos.FeedIterator Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ToStreamIterator[T](System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ToStreamIterator[T](System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.FeedIterator`1[T] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ToFeedIterator[T](System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean FullTextContains(System.Object, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator`1[T] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ToFeedIterator[T](System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions+VectorDistanceOptions": { + "Type": "NestedType", + "Attributes": [], + "MethodInfo": null }, - "Boolean FullTextContainsAll(System.Object, System.String[])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Microsoft.Azure.Cosmos.QueryDefinition Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ToQueryDefinition[T](System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean FullTextContainsAll(System.Object, System.String[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.QueryDefinition Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ToQueryDefinition[T](System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, - "Boolean FullTextContainsAny(System.Object, System.String[])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ArrayContainsAll[T](System.Collections.Generic.IEnumerable`1[T], System.Object[])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean FullTextContainsAny(System.Object, System.String[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ArrayContainsAll[T](System.Collections.Generic.IEnumerable`1[T], System.Object[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, - "Boolean IsArray(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ArrayContainsAny[T](System.Collections.Generic.IEnumerable`1[T], System.Object[])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean IsArray(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ArrayContainsAny[T](System.Collections.Generic.IEnumerable`1[T], System.Object[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, - "Boolean IsBool(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.FullTextContains(System.Object, System.String)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean IsBool(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.FullTextContains(System.Object, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean IsDefined(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.FullTextContainsAll(System.Object, System.String[])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean IsDefined(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.FullTextContainsAll(System.Object, System.String[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean IsNull(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.FullTextContainsAny(System.Object, System.String[])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean IsNull(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.FullTextContainsAny(System.Object, System.String[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean IsNumber(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsArray(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean IsNumber(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsArray(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean IsObject(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsBool(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean IsObject(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsBool(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean IsPrimitive(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsDefined(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean IsPrimitive(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsDefined(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean IsString(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsNull(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean IsString(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsNull(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean RegexMatch(System.Object, System.String, System.String)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsNumber(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean RegexMatch(System.Object, System.String, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsNumber(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean RegexMatch(System.Object, System.String)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsObject(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Boolean RegexMatch(System.Object, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsObject(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Double FullTextScore[TSource](TSource, System.String[])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsPrimitive(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Double FullTextScore[TSource](TSource, System.String[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsPrimitive(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Double RRF(Double[])": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsString(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", - "Attributes": [], - "MethodInfo": "Double RRF(Double[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.IsString(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Double VectorDistance(Byte[], Byte[], Boolean, VectorDistanceOptions)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.RegexMatch(System.Object, System.String, System.String)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Double VectorDistance(Byte[], Byte[], Boolean, VectorDistanceOptions);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.RegexMatch(System.Object, System.String, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Double VectorDistance(SByte[], SByte[], Boolean, VectorDistanceOptions)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.RegexMatch(System.Object, System.String)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Double VectorDistance(SByte[], SByte[], Boolean, VectorDistanceOptions);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.RegexMatch(System.Object, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Double VectorDistance(Single[], Single[], Boolean, VectorDistanceOptions)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.FullTextScore[TSource](TSource, System.String[])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Double VectorDistance(Single[], Single[], Boolean, VectorDistanceOptions);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.FullTextScore[TSource](TSource, System.String[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + }, + "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.RRF(System.Double[], System.Double[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.RRF(System.Double[], System.Double[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Int32 DocumentId(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.RRF(System.Double[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.RRF(System.Double[]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.VectorDistance(System.Byte[], System.Byte[], System.Boolean, Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions+VectorDistanceOptions)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Int32 DocumentId(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.VectorDistance(System.Byte[], System.Byte[], System.Boolean, Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions+VectorDistanceOptions);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.FeedIterator ToStreamIterator[T](System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.VectorDistance(System.SByte[], System.SByte[], System.Boolean, Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions+VectorDistanceOptions)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator ToStreamIterator[T](System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.VectorDistance(System.SByte[], System.SByte[], System.Boolean, Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions+VectorDistanceOptions);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.FeedIterator`1[T] ToFeedIterator[T](System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.VectorDistance(System.Single[], System.Single[], System.Boolean, Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions+VectorDistanceOptions)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Microsoft.Azure.Cosmos.FeedIterator`1[T] ToFeedIterator[T](System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Double Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.VectorDistance(System.Single[], System.Single[], System.Boolean, Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions+VectorDistanceOptions);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions+VectorDistanceOptions": { - "Type": "NestedType", - "Attributes": [], - "MethodInfo": null + "System.Int32 Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.DocumentId(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Type": "Method", + "Attributes": [ + "ExtensionAttribute" + ], + "MethodInfo": "System.Int32 Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.DocumentId(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.QueryDefinition ToQueryDefinition[T](System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Linq.IOrderedQueryable`1[TSource] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.OrderByRank[TSource, TKey](System.Linq.IQueryable`1[TSource], System.Linq.Expressions.Expression`1[System.Func`2[TSource,TKey]])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Microsoft.Azure.Cosmos.QueryDefinition ToQueryDefinition[T](System.Linq.IQueryable`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Linq.IOrderedQueryable`1[TSource] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.OrderByRank[TSource, TKey](System.Linq.IQueryable`1[TSource], System.Linq.Expressions.Expression`1[System.Func`2[TSource,TKey]]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, - "System.Linq.IOrderedQueryable`1[TSource] OrderByRank[TSource,TKey](System.Linq.IQueryable`1[TSource], System.Linq.Expressions.Expression`1[System.Func`2[TSource,TKey]])[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.String Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.GetIndexMetrics[T](Microsoft.Azure.Cosmos.Response`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Linq.IOrderedQueryable`1[TSource] OrderByRank[TSource,TKey](System.Linq.IQueryable`1[TSource], System.Linq.Expressions.Expression`1[System.Func`2[TSource,TKey]]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.String Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.GetIndexMetrics[T](Microsoft.Azure.Cosmos.Response`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Decimal]] AverageAsync(System.Linq.IQueryable`1[System.Decimal], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Decimal]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Decimal], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Decimal]] AverageAsync(System.Linq.IQueryable`1[System.Decimal], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Decimal]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Decimal], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Decimal]] SumAsync(System.Linq.IQueryable`1[System.Decimal], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Decimal]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Decimal], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Decimal]] SumAsync(System.Linq.IQueryable`1[System.Decimal], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Decimal]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Decimal], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] AverageAsync(System.Linq.IQueryable`1[System.Double], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Double], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] AverageAsync(System.Linq.IQueryable`1[System.Double], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Double], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] AverageAsync(System.Linq.IQueryable`1[System.Int32], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Int32], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] AverageAsync(System.Linq.IQueryable`1[System.Int32], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Int32], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] AverageAsync(System.Linq.IQueryable`1[System.Int64], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Int64], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] AverageAsync(System.Linq.IQueryable`1[System.Int64], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Int64], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] SumAsync(System.Linq.IQueryable`1[System.Double], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Double], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] SumAsync(System.Linq.IQueryable`1[System.Double], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Double]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Double], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int32]] CountAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int32]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.CountAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int32]] CountAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int32]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.CountAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int32]] SumAsync(System.Linq.IQueryable`1[System.Int32], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int32]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Int32], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int32]] SumAsync(System.Linq.IQueryable`1[System.Int32], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int32]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Int32], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int64]] SumAsync(System.Linq.IQueryable`1[System.Int64], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int64]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Int64], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int64]] SumAsync(System.Linq.IQueryable`1[System.Int64], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Int64]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Int64], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Decimal]]] AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Decimal]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Decimal]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Decimal]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Decimal]]] AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Decimal]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Decimal]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Decimal]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Decimal]]] SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Decimal]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Decimal]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Decimal]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Decimal]]] SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Decimal]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Decimal]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Decimal]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Double]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Double]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Double]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Double]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int32]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int32]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int32]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int32]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int64]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int64]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int64]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int64]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Double]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Double]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Double]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Double]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Double]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Int32]]] SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int32]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Int32]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int32]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Int32]]] SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int32]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Int32]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int32]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Int64]]] SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int64]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Int64]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int64]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Int64]]] SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int64]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Int64]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Int64]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Single]]] AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Single]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Single]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Single]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Single]]] AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Single]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Single]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Single]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Single]]] SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Single]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Single]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Single]], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Single]]] SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Single]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Nullable`1[System.Single]]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Nullable`1[System.Single]], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Single]] AverageAsync(System.Linq.IQueryable`1[System.Single], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Single]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Single], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Single]] AverageAsync(System.Linq.IQueryable`1[System.Single], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Single]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.AverageAsync(System.Linq.IQueryable`1[System.Single], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Single]] SumAsync(System.Linq.IQueryable`1[System.Single], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Single]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Single], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Single]] SumAsync(System.Linq.IQueryable`1[System.Single], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[System.Single]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.SumAsync(System.Linq.IQueryable`1[System.Single], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[TSource]] MaxAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[TSource]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.MaxAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[TSource]] MaxAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[TSource]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.MaxAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[TSource]] MinAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[TSource]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.MinAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[TSource]] MinAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Response`1[TSource]] Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.MinAsync[TSource](System.Linq.IQueryable`1[TSource], System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": { @@ -6643,7 +6690,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_DataType(System.Nullable`1[Microsoft.Azure.Cosmos.VectorDataType])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -6719,7 +6766,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_DataType(System.Nullable`1[Microsoft.Azure.Cosmos.VectorDataType])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -6796,16 +6843,6 @@ "Attributes": [], "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean op_Equality(Microsoft.Azure.Cosmos.PartitionKey, Microsoft.Azure.Cosmos.PartitionKey)": { - "Type": "Method", - "Attributes": [], - "MethodInfo": "Boolean op_Equality(Microsoft.Azure.Cosmos.PartitionKey, Microsoft.Azure.Cosmos.PartitionKey);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, - "Boolean op_Inequality(Microsoft.Azure.Cosmos.PartitionKey, Microsoft.Azure.Cosmos.PartitionKey)": { - "Type": "Method", - "Attributes": [], - "MethodInfo": "Boolean op_Inequality(Microsoft.Azure.Cosmos.PartitionKey, Microsoft.Azure.Cosmos.PartitionKey);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" - }, "Int32 GetHashCode()": { "Type": "Method", "Attributes": [], @@ -6821,6 +6858,16 @@ "Attributes": [], "MethodInfo": "Microsoft.Azure.Cosmos.PartitionKey Null;IsInitOnly:True;IsStatic:True;" }, + "System.Boolean Microsoft.Azure.Cosmos.PartitionKey.op_Equality(Microsoft.Azure.Cosmos.PartitionKey, Microsoft.Azure.Cosmos.PartitionKey)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.PartitionKey.op_Equality(Microsoft.Azure.Cosmos.PartitionKey, Microsoft.Azure.Cosmos.PartitionKey);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Boolean Microsoft.Azure.Cosmos.PartitionKey.op_Inequality(Microsoft.Azure.Cosmos.PartitionKey, Microsoft.Azure.Cosmos.PartitionKey)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Boolean Microsoft.Azure.Cosmos.PartitionKey.op_Inequality(Microsoft.Azure.Cosmos.PartitionKey, Microsoft.Azure.Cosmos.PartitionKey);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "System.String SystemKeyName": { "Type": "Field", "Attributes": [], @@ -6839,17 +6886,17 @@ "Void .ctor(Boolean)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Boolean), Void .ctor(Boolean)]" + "MethodInfo": "Void .ctor(Boolean)" }, "Void .ctor(Double)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Double), Void .ctor(Double)]" + "MethodInfo": "Void .ctor(Double)" }, "Void .ctor(System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String), Void .ctor(System.String)]" + "MethodInfo": "Void .ctor(System.String)" } }, "NestedTypes": {} @@ -6890,7 +6937,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} @@ -6934,7 +6981,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_FilterPredicate(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -6973,40 +7020,40 @@ "Attributes": [], "MethodInfo": "Boolean TrySerializeValueParameter(Microsoft.Azure.Cosmos.CosmosSerializer, System.IO.Stream ByRef);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.PatchOperation Add[T](System.String, T)": { + "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Add[T](System.String, T)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Add[T](System.String, T);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Add[T](System.String, T);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.PatchOperation Increment(System.String, Double)": { + "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Increment(System.String, System.Double)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Increment(System.String, Double);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Increment(System.String, System.Double);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.PatchOperation Increment(System.String, Int64)": { + "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Increment(System.String, System.Int64)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Increment(System.String, Int64);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Increment(System.String, System.Int64);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.PatchOperation Move(System.String, System.String)": { + "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Move(System.String, System.String)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Move(System.String, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Move(System.String, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.PatchOperation Remove(System.String)": { + "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Remove(System.String)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Remove(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Remove(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.PatchOperation Replace[T](System.String, T)": { + "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Replace[T](System.String, T)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Replace[T](System.String, T);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Replace[T](System.String, T);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.PatchOperation Set[T](System.String, T)": { + "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Set[T](System.String, T)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Set[T](System.String, T);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.PatchOperation Microsoft.Azure.Cosmos.PatchOperation.Set[T](System.String, T);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:True;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.PatchOperationType get_OperationType()": { "Type": "Method", @@ -7216,7 +7263,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_ts\")]": { + "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_ts\")]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -7224,7 +7271,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] LastModified;CanRead:True;CanWrite:True;System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -7280,14 +7327,14 @@ ], "MethodInfo": "System.String ResourceUri;CanRead:True;CanWrite:True;System.String get_ResourceUri();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" ], "MethodInfo": "System.String SelfLink;CanRead:True;CanWrite:True;System.String get_SelfLink();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String Token[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_token\")]": { + "System.String Token[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_token\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -7297,12 +7344,12 @@ "Void .ctor(System.String, Microsoft.Azure.Cosmos.PermissionMode, Microsoft.Azure.Cosmos.Container, Microsoft.Azure.Cosmos.PartitionKey, System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, Microsoft.Azure.Cosmos.PermissionMode, Microsoft.Azure.Cosmos.Container, Microsoft.Azure.Cosmos.PartitionKey, System.String), Void .ctor(System.String, Microsoft.Azure.Cosmos.PermissionMode, Microsoft.Azure.Cosmos.Container, Microsoft.Azure.Cosmos.PartitionKey, System.String)]" + "MethodInfo": "Void .ctor(System.String, Microsoft.Azure.Cosmos.PermissionMode, Microsoft.Azure.Cosmos.Container, Microsoft.Azure.Cosmos.PartitionKey, System.String)" }, "Void .ctor(System.String, Microsoft.Azure.Cosmos.PermissionMode, Microsoft.Azure.Cosmos.Container, System.Nullable`1[Microsoft.Azure.Cosmos.PartitionKey])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, Microsoft.Azure.Cosmos.PermissionMode, Microsoft.Azure.Cosmos.Container, System.Nullable`1[Microsoft.Azure.Cosmos.PartitionKey]), Void .ctor(System.String, Microsoft.Azure.Cosmos.PermissionMode, Microsoft.Azure.Cosmos.Container, System.Nullable`1[Microsoft.Azure.Cosmos.PartitionKey])]" + "MethodInfo": "Void .ctor(System.String, Microsoft.Azure.Cosmos.PermissionMode, Microsoft.Azure.Cosmos.Container, System.Nullable`1[Microsoft.Azure.Cosmos.PartitionKey])" }, "Void set_ResourcePartitionKey(System.Nullable`1[Microsoft.Azure.Cosmos.PartitionKey])": { "Type": "Method", @@ -7356,10 +7403,10 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Permission get_Permission();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Permission op_Implicit(Microsoft.Azure.Cosmos.PermissionResponse)": { + "Microsoft.Azure.Cosmos.Permission Microsoft.Azure.Cosmos.PermissionResponse.op_Implicit(Microsoft.Azure.Cosmos.PermissionResponse)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Permission op_Implicit(Microsoft.Azure.Cosmos.PermissionResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Permission Microsoft.Azure.Cosmos.PermissionResponse.op_Implicit(Microsoft.Azure.Cosmos.PermissionResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.Permission Permission": { "Type": "Property", @@ -7480,7 +7527,7 @@ ], "MethodInfo": "System.String get_QueryText();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String QueryText[Newtonsoft.Json.JsonPropertyAttribute(Order = 0, PropertyName = \"query\")]": { + "System.String QueryText[Newtonsoft.Json.JsonPropertyAttribute(Order = (Int32)0, PropertyName = \"query\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -7490,7 +7537,7 @@ "Void .ctor(System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String), Void .ctor(System.String)]" + "MethodInfo": "Void .ctor(System.String)" } }, "NestedTypes": {} @@ -7522,6 +7569,18 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.DedicatedGatewayRequestOptions get_DedicatedGatewayRequestOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Microsoft.Azure.Cosmos.FullTextScoreScope FullTextScoreScope": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.FullTextScoreScope FullTextScoreScope;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.FullTextScoreScope get_FullTextScoreScope();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_FullTextScoreScope(Microsoft.Azure.Cosmos.FullTextScoreScope);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.FullTextScoreScope get_FullTextScoreScope()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.FullTextScoreScope get_FullTextScoreScope();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -7667,7 +7726,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ConsistencyLevel(System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel])": { "Type": "Method", @@ -7702,6 +7761,13 @@ ], "MethodInfo": "Void set_EnableScanInQuery(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Void set_FullTextScoreScope(Microsoft.Azure.Cosmos.FullTextScoreScope)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_FullTextScoreScope(Microsoft.Azure.Cosmos.FullTextScoreScope);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void set_MaxBufferedItemCount(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -7822,7 +7888,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ConsistencyLevel(System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel])": { "Type": "Method", @@ -8017,6 +8083,11 @@ "Attributes": [], "MethodInfo": "System.String GermanyWestCentral;IsInitOnly:False;IsStatic:True;" }, + "System.String IndiaSouthCentral": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String IndiaSouthCentral;IsInitOnly:False;IsStatic:True;" + }, "System.String IndonesiaCentral": { "Type": "Field", "Attributes": [], @@ -8092,6 +8163,11 @@ "Attributes": [], "MethodInfo": "System.String NorthCentralUS;IsInitOnly:False;IsStatic:True;" }, + "System.String NortheastUS5": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String NortheastUS5;IsInitOnly:False;IsStatic:True;" + }, "System.String NorthEurope": { "Type": "Field", "Attributes": [], @@ -8117,6 +8193,16 @@ "Attributes": [], "MethodInfo": "System.String QatarCentral;IsInitOnly:False;IsStatic:True;" }, + "System.String SingaporeCentral": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String SingaporeCentral;IsInitOnly:False;IsStatic:True;" + }, + "System.String SingaporeNorth": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String SingaporeNorth;IsInitOnly:False;IsStatic:True;" + }, "System.String SouthAfricaNorth": { "Type": "Field", "Attributes": [], @@ -8395,12 +8481,12 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void .ctor(System.Net.Http.HttpMethod, System.Uri)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Net.Http.HttpMethod, System.Uri), Void .ctor(System.Net.Http.HttpMethod, System.Uri)]" + "MethodInfo": "Void .ctor(System.Net.Http.HttpMethod, System.Uri)" }, "Void Dispose()": { "Type": "Method", @@ -8440,7 +8526,7 @@ "Attributes": [], "MethodInfo": "System.String get_IfNoneMatchEtag();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String IfMatchEtag[System.ObsoleteAttribute(\"IfMatchEtag is inherited from the base class but not used.\")]-[System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)1)]": { + "System.String IfMatchEtag[System.ObsoleteAttribute(\"IfMatchEtag is inherited from the base class but not used.\")]-[System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never = 1)]": { "Type": "Property", "Attributes": [ "EditorBrowsableAttribute", @@ -8448,7 +8534,7 @@ ], "MethodInfo": "System.String IfMatchEtag;CanRead:True;CanWrite:True;System.String get_IfMatchEtag();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_IfMatchEtag(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String IfNoneMatchEtag[System.ObsoleteAttribute(\"IfNoneMatchEtag is inherited from the base class but not used.\")]-[System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)1)]": { + "System.String IfNoneMatchEtag[System.ObsoleteAttribute(\"IfNoneMatchEtag is inherited from the base class but not used.\")]-[System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never = 1)]": { "Type": "Property", "Attributes": [ "EditorBrowsableAttribute", @@ -8459,7 +8545,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_IfMatchEtag(System.String)": { "Type": "Method", @@ -8497,7 +8583,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_PopulateQuotaInfo(Boolean)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -8529,7 +8615,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_FilterPredicate(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -8628,7 +8714,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ConsistencyLevel(System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel])": { "Type": "Method", @@ -8698,7 +8784,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_FilterPredicate(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -8737,6 +8823,18 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.DedicatedGatewayRequestOptions get_DedicatedGatewayRequestOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Microsoft.Azure.Cosmos.FullTextScoreScope FullTextScoreScope": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.FullTextScoreScope FullTextScoreScope;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.FullTextScoreScope get_FullTextScoreScope();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_FullTextScoreScope(Microsoft.Azure.Cosmos.FullTextScoreScope);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.FullTextScoreScope get_FullTextScoreScope()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.FullTextScoreScope get_FullTextScoreScope();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -8882,7 +8980,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ConsistencyLevel(System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel])": { "Type": "Method", @@ -8917,6 +9015,13 @@ ], "MethodInfo": "Void set_EnableScanInQuery(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Void set_FullTextScoreScope(Microsoft.Azure.Cosmos.FullTextScoreScope)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_FullTextScoreScope(Microsoft.Azure.Cosmos.FullTextScoreScope);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void set_MaxBufferedItemCount(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -9011,7 +9116,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ConsistencyLevel(System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel])": { "Type": "Method", @@ -9068,7 +9173,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ConsistencyLevel(System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel])": { "Type": "Method", @@ -9112,7 +9217,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_FilterPredicate(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -9153,7 +9258,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_EnableContentResponseOnWrite(System.Nullable`1[System.Boolean])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -9190,7 +9295,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_FilterPredicate(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -9230,7 +9335,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ConsistencyLevel(System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel])": { "Type": "Method", @@ -9249,6 +9354,18 @@ } }, "Members": { + "Microsoft.Azure.Cosmos.AvailabilityStrategy AvailabilityStrategy": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy AvailabilityStrategy;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.AvailabilityStrategy get_AvailabilityStrategy();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_AvailabilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.AvailabilityStrategy get_AvailabilityStrategy()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy get_AvailabilityStrategy();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Microsoft.Azure.Cosmos.CosmosThresholdOptions CosmosThresholdOptions": { "Type": "Property", "Attributes": [], @@ -9341,7 +9458,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_AddRequestHeaders(System.Action`1[Microsoft.Azure.Cosmos.Headers])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -9350,6 +9467,13 @@ ], "MethodInfo": "Void set_AddRequestHeaders(System.Action`1[Microsoft.Azure.Cosmos.Headers]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Void set_AvailabilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_AvailabilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void set_CosmosThresholdOptions(Microsoft.Azure.Cosmos.CosmosThresholdOptions)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -9463,10 +9587,10 @@ "Attributes": [], "MethodInfo": "T get_Resource();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "T op_Implicit(Microsoft.Azure.Cosmos.Response`1[T])": { + "T Microsoft.Azure.Cosmos.Response`1.op_Implicit(Microsoft.Azure.Cosmos.Response`1[T])": { "Type": "Method", "Attributes": [], - "MethodInfo": "T op_Implicit(Microsoft.Azure.Cosmos.Response`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "T Microsoft.Azure.Cosmos.Response`1.op_Implicit(Microsoft.Azure.Cosmos.Response`1[T]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "T Resource": { "Type": "Property", @@ -9583,12 +9707,12 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void .ctor(System.Net.HttpStatusCode, Microsoft.Azure.Cosmos.RequestMessage, System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Net.HttpStatusCode, Microsoft.Azure.Cosmos.RequestMessage, System.String), Void .ctor(System.Net.HttpStatusCode, Microsoft.Azure.Cosmos.RequestMessage, System.String)]" + "MethodInfo": "Void .ctor(System.Net.HttpStatusCode, Microsoft.Azure.Cosmos.RequestMessage, System.String)" }, "Void Dispose()": { "Type": "Method", @@ -9923,7 +10047,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_ts\")]": { + "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_ts\")]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -9938,7 +10062,7 @@ ], "MethodInfo": "System.String Body;CanRead:True;CanWrite:True;System.String get_Body();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Body(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -9976,7 +10100,7 @@ ], "MethodInfo": "System.String Id;CanRead:True;CanWrite:True;System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Id(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -9986,12 +10110,12 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void .ctor(System.String, System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.String), Void .ctor(System.String, System.String)]" + "MethodInfo": "Void .ctor(System.String, System.String)" }, "Void set_Body(System.String)": { "Type": "Method", @@ -10046,7 +10170,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ConsistencyLevel(System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel])": { "Type": "Method", @@ -10114,10 +10238,10 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Scripts.StoredProcedureProperties get_Resource();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Scripts.StoredProcedureProperties op_Implicit(Microsoft.Azure.Cosmos.Scripts.StoredProcedureResponse)": { + "Microsoft.Azure.Cosmos.Scripts.StoredProcedureProperties Microsoft.Azure.Cosmos.Scripts.StoredProcedureResponse.op_Implicit(Microsoft.Azure.Cosmos.Scripts.StoredProcedureResponse)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Scripts.StoredProcedureProperties op_Implicit(Microsoft.Azure.Cosmos.Scripts.StoredProcedureResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Scripts.StoredProcedureProperties Microsoft.Azure.Cosmos.Scripts.StoredProcedureResponse.op_Implicit(Microsoft.Azure.Cosmos.Scripts.StoredProcedureResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.Scripts.StoredProcedureProperties Resource": { "Type": "Property", @@ -10250,7 +10374,7 @@ ], "MethodInfo": "System.String Body;CanRead:True;CanWrite:True;System.String get_Body();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Body(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -10292,7 +10416,7 @@ ], "MethodInfo": "System.String Id;CanRead:True;CanWrite:True;System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Id(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -10302,7 +10426,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Body(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -10379,10 +10503,10 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Scripts.TriggerProperties get_Resource();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Scripts.TriggerProperties op_Implicit(Microsoft.Azure.Cosmos.Scripts.TriggerResponse)": { + "Microsoft.Azure.Cosmos.Scripts.TriggerProperties Microsoft.Azure.Cosmos.Scripts.TriggerResponse.op_Implicit(Microsoft.Azure.Cosmos.Scripts.TriggerResponse)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Scripts.TriggerProperties op_Implicit(Microsoft.Azure.Cosmos.Scripts.TriggerResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Scripts.TriggerProperties Microsoft.Azure.Cosmos.Scripts.TriggerResponse.op_Implicit(Microsoft.Azure.Cosmos.Scripts.TriggerResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.Scripts.TriggerProperties Resource": { "Type": "Property", @@ -10455,7 +10579,7 @@ ], "MethodInfo": "System.String Body;CanRead:True;CanWrite:True;System.String get_Body();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Body(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -10497,7 +10621,7 @@ ], "MethodInfo": "System.String Id;CanRead:True;CanWrite:True;System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Id(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -10507,7 +10631,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_Body(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -10570,10 +10694,10 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionProperties get_Resource();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionProperties op_Implicit(Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionResponse)": { + "Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionProperties Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionResponse.op_Implicit(Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionResponse)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionProperties op_Implicit(Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionProperties Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionResponse.op_Implicit(Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.Scripts.UserDefinedFunctionProperties Resource": { "Type": "Property", @@ -10872,7 +10996,7 @@ "Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.Position)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.Position), Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.Position)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.Position)" } }, "NestedTypes": {} @@ -10968,22 +11092,22 @@ "Microsoft.Azure.Cosmos.Spatial.Crs Default": { "Type": "Property", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.Crs Default;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Spatial.Crs get_Default();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.Crs Default;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Spatial.Crs Microsoft.Azure.Cosmos.Spatial.Crs.get_Default();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Spatial.Crs get_Default()": { + "Microsoft.Azure.Cosmos.Spatial.Crs Microsoft.Azure.Cosmos.Spatial.Crs.get_Default()": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.Crs get_Default();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.Crs Microsoft.Azure.Cosmos.Spatial.Crs.get_Default();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Spatial.Crs get_Unspecified()": { + "Microsoft.Azure.Cosmos.Spatial.Crs Microsoft.Azure.Cosmos.Spatial.Crs.get_Unspecified()": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.Crs get_Unspecified();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.Crs Microsoft.Azure.Cosmos.Spatial.Crs.get_Unspecified();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.Spatial.Crs Unspecified": { "Type": "Property", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.Crs Unspecified;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Spatial.Crs get_Unspecified();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.Crs Unspecified;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.Spatial.Crs Microsoft.Azure.Cosmos.Spatial.Crs.get_Unspecified();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.Spatial.CrsType get_Type()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -10999,20 +11123,20 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.CrsType Type;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.Spatial.CrsType get_Type();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Spatial.LinkedCrs Linked(System.String, System.String)": { + "Microsoft.Azure.Cosmos.Spatial.LinkedCrs Microsoft.Azure.Cosmos.Spatial.Crs.Linked(System.String, System.String)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.LinkedCrs Linked(System.String, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.LinkedCrs Microsoft.Azure.Cosmos.Spatial.Crs.Linked(System.String, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Spatial.LinkedCrs Linked(System.String)": { + "Microsoft.Azure.Cosmos.Spatial.LinkedCrs Microsoft.Azure.Cosmos.Spatial.Crs.Linked(System.String)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.LinkedCrs Linked(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.LinkedCrs Microsoft.Azure.Cosmos.Spatial.Crs.Linked(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Spatial.NamedCrs Named(System.String)": { + "Microsoft.Azure.Cosmos.Spatial.NamedCrs Microsoft.Azure.Cosmos.Spatial.Crs.Named(System.String)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.NamedCrs Named(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.NamedCrs Microsoft.Azure.Cosmos.Spatial.Crs.Named(System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -11070,7 +11194,7 @@ ], "MethodInfo": "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.Position] get_Positions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.Position] Positions[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = 1, Required = 2)]": { + "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.Position] Positions[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = (Int32)1, Required = Required.Always = 2)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11081,12 +11205,12 @@ "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position], Microsoft.Azure.Cosmos.Spatial.GeometryParams)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position], Microsoft.Azure.Cosmos.Spatial.GeometryParams), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position], Microsoft.Azure.Cosmos.Spatial.GeometryParams)]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position], Microsoft.Azure.Cosmos.Spatial.GeometryParams)" }, "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position]), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])" } }, "NestedTypes": {} @@ -11116,7 +11240,7 @@ ], "MethodInfo": "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates] get_Polygons();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates] Polygons[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = 1, Required = 2)]": { + "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates] Polygons[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = (Int32)1, Required = Required.Always = 2)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11127,12 +11251,12 @@ "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates], Microsoft.Azure.Cosmos.Spatial.GeometryParams)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates], Microsoft.Azure.Cosmos.Spatial.GeometryParams), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates], Microsoft.Azure.Cosmos.Spatial.GeometryParams)]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates], Microsoft.Azure.Cosmos.Spatial.GeometryParams)" }, "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates]), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates])" } }, "NestedTypes": {} @@ -11162,7 +11286,7 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.Position get_Position();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Spatial.Position Position[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = 1, Required = 2)]": { + "Microsoft.Azure.Cosmos.Spatial.Position Position[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = (Int32)1, Required = Required.Always = 2)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11173,17 +11297,17 @@ "Void .ctor(Double, Double)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Double, Double), Void .ctor(Double, Double)]" + "MethodInfo": "Void .ctor(Double, Double)" }, "Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.GeometryParams)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.GeometryParams), Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.GeometryParams)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.GeometryParams)" }, "Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position), Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position)" } }, "NestedTypes": {} @@ -11213,7 +11337,7 @@ ], "MethodInfo": "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.LinearRing] get_Rings();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.LinearRing] Rings[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = 1, Required = 2)]": { + "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.LinearRing] Rings[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = (Int32)1, Required = Required.Always = 2)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11224,17 +11348,17 @@ "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing], Microsoft.Azure.Cosmos.Spatial.GeometryParams)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing], Microsoft.Azure.Cosmos.Spatial.GeometryParams), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing], Microsoft.Azure.Cosmos.Spatial.GeometryParams)]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing], Microsoft.Azure.Cosmos.Spatial.GeometryParams)" }, "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing]), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing])" }, "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position]), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])" } }, "NestedTypes": {} @@ -11271,7 +11395,7 @@ "Attributes": [], "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Spatial.BoundingBox BoundingBox[System.Runtime.Serialization.DataMemberAttribute(Name = \"bbox\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"bbox\", DefaultValueHandling = 1, Order = 3)]": { + "Microsoft.Azure.Cosmos.Spatial.BoundingBox BoundingBox[System.Runtime.Serialization.DataMemberAttribute(Name = \"bbox\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"bbox\", DefaultValueHandling = DefaultValueHandling.Ignore = 1, Order = (Int32)3)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11303,7 +11427,7 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.GeometryType get_Type();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Spatial.GeometryType Type[System.Runtime.Serialization.DataMemberAttribute(Name = \"type\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"type\", Order = 0, Required = 2)]-[Newtonsoft.Json.JsonConverterAttribute(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]": { + "Microsoft.Azure.Cosmos.Spatial.GeometryType Type[System.Runtime.Serialization.DataMemberAttribute(Name = \"type\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"type\", Order = (Int32)0, Required = Required.Always = 2)]-[Newtonsoft.Json.JsonConverterAttribute(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11383,7 +11507,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_AdditionalProperties(System.Collections.Generic.IDictionary`2[System.String,System.Object])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -11511,7 +11635,7 @@ ], "MethodInfo": "Boolean get_IsValid();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean IsValid[System.Runtime.Serialization.DataMemberAttribute(Name = \"valid\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"valid\", Order = 0, Required = 2)]": { + "Boolean IsValid[System.Runtime.Serialization.DataMemberAttribute(Name = \"valid\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"valid\", Order = (Int32)0, Required = Required.Always = 2)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11526,7 +11650,7 @@ ], "MethodInfo": "System.String get_Reason();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String Reason[System.Runtime.Serialization.DataMemberAttribute(Name = \"reason\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"reason\", Order = 1)]": { + "System.String Reason[System.Runtime.Serialization.DataMemberAttribute(Name = \"reason\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"reason\", Order = (Int32)1)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11537,7 +11661,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} @@ -11577,7 +11701,7 @@ "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position]), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])" } }, "NestedTypes": {} @@ -11607,7 +11731,7 @@ ], "MethodInfo": "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.Position] get_Positions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.Position] Positions[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = 1, Required = 2)]": { + "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.Position] Positions[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = (Int32)1, Required = Required.Always = 2)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11618,12 +11742,12 @@ "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position], Microsoft.Azure.Cosmos.Spatial.GeometryParams)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position], Microsoft.Azure.Cosmos.Spatial.GeometryParams), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position], Microsoft.Azure.Cosmos.Spatial.GeometryParams)]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position], Microsoft.Azure.Cosmos.Spatial.GeometryParams)" }, "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position]), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])" } }, "NestedTypes": {} @@ -11702,7 +11826,7 @@ ], "MethodInfo": "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates] get_Polygons();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates] Polygons[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = 1, Required = 2)]": { + "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates] Polygons[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = (Int32)1, Required = Required.Always = 2)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11713,12 +11837,12 @@ "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates], Microsoft.Azure.Cosmos.Spatial.GeometryParams)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates], Microsoft.Azure.Cosmos.Spatial.GeometryParams), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates], Microsoft.Azure.Cosmos.Spatial.GeometryParams)]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates], Microsoft.Azure.Cosmos.Spatial.GeometryParams)" }, "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates]), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.PolygonCoordinates])" } }, "NestedTypes": {} @@ -11783,7 +11907,7 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.Spatial.Position get_Position();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.Spatial.Position Position[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = 1, Required = 2)]": { + "Microsoft.Azure.Cosmos.Spatial.Position Position[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = (Int32)1, Required = Required.Always = 2)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11794,17 +11918,17 @@ "Void .ctor(Double, Double)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Double, Double), Void .ctor(Double, Double)]" + "MethodInfo": "Void .ctor(Double, Double)" }, "Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.GeometryParams)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.GeometryParams), Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.GeometryParams)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position, Microsoft.Azure.Cosmos.Spatial.GeometryParams)" }, "Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position), Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position)]" + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Spatial.Position)" } }, "NestedTypes": {} @@ -11834,7 +11958,7 @@ ], "MethodInfo": "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.LinearRing] get_Rings();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.LinearRing] Rings[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = 1, Required = 2)]": { + "System.Collections.ObjectModel.ReadOnlyCollection`1[Microsoft.Azure.Cosmos.Spatial.LinearRing] Rings[System.Runtime.Serialization.DataMemberAttribute(Name = \"coordinates\")]-[Newtonsoft.Json.JsonPropertyAttribute(\"coordinates\", Order = (Int32)1, Required = Required.Always = 2)]": { "Type": "Property", "Attributes": [ "DataMemberAttribute", @@ -11845,17 +11969,17 @@ "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing], Microsoft.Azure.Cosmos.Spatial.GeometryParams)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing], Microsoft.Azure.Cosmos.Spatial.GeometryParams), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing], Microsoft.Azure.Cosmos.Spatial.GeometryParams)]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing], Microsoft.Azure.Cosmos.Spatial.GeometryParams)" }, "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing]), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing])" }, "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position]), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.Position])" } }, "NestedTypes": {} @@ -11895,7 +12019,7 @@ "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing]), Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[Microsoft.Azure.Cosmos.Spatial.LinearRing])" } }, "NestedTypes": {} @@ -11965,17 +12089,17 @@ "Void .ctor(Double, Double, System.Nullable`1[System.Double])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Double, Double, System.Nullable`1[System.Double]), Void .ctor(Double, Double, System.Nullable`1[System.Double])]" + "MethodInfo": "Void .ctor(Double, Double, System.Nullable`1[System.Double])" }, "Void .ctor(Double, Double)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(Double, Double), Void .ctor(Double, Double)]" + "MethodInfo": "Void .ctor(Double, Double)" }, "Void .ctor(System.Collections.Generic.IList`1[System.Double])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.Generic.IList`1[System.Double]), Void .ctor(System.Collections.Generic.IList`1[System.Double])]" + "MethodInfo": "Void .ctor(System.Collections.Generic.IList`1[System.Double])" } }, "NestedTypes": {} @@ -11983,7 +12107,7 @@ "Microsoft.Azure.Cosmos.SpatialPath;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "Microsoft.Azure.Cosmos.BoundingBoxProperties BoundingBox[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"boundingBox\")]": { + "Microsoft.Azure.Cosmos.BoundingBoxProperties BoundingBox[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"boundingBox\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -12026,7 +12150,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_BoundingBox(Microsoft.Azure.Cosmos.BoundingBoxProperties)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -12079,15 +12203,15 @@ "Microsoft.Azure.Cosmos.ThroughputProperties;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "Microsoft.Azure.Cosmos.ThroughputProperties CreateAutoscaleThroughput(Int32)": { + "Microsoft.Azure.Cosmos.ThroughputProperties Microsoft.Azure.Cosmos.ThroughputProperties.CreateAutoscaleThroughput(System.Int32)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ThroughputProperties CreateAutoscaleThroughput(Int32);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ThroughputProperties Microsoft.Azure.Cosmos.ThroughputProperties.CreateAutoscaleThroughput(System.Int32);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.ThroughputProperties CreateManualThroughput(Int32)": { + "Microsoft.Azure.Cosmos.ThroughputProperties Microsoft.Azure.Cosmos.ThroughputProperties.CreateManualThroughput(System.Int32)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ThroughputProperties CreateManualThroughput(Int32);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ThroughputProperties Microsoft.Azure.Cosmos.ThroughputProperties.CreateManualThroughput(System.Int32);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "System.Nullable`1[System.DateTime] get_LastModified()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -12096,7 +12220,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_ts\")]": { + "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_ts\")]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -12128,7 +12252,7 @@ ], "MethodInfo": "System.Nullable`1[System.Int32] Throughput;CanRead:True;CanWrite:True;System.Nullable`1[System.Int32] get_Throughput();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -12149,7 +12273,7 @@ ], "MethodInfo": "System.String get_SelfLink();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -12203,10 +12327,10 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.ThroughputProperties get_Resource();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.ThroughputProperties op_Implicit(Microsoft.Azure.Cosmos.ThroughputResponse)": { + "Microsoft.Azure.Cosmos.ThroughputProperties Microsoft.Azure.Cosmos.ThroughputResponse.op_Implicit(Microsoft.Azure.Cosmos.ThroughputResponse)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.ThroughputProperties op_Implicit(Microsoft.Azure.Cosmos.ThroughputResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.ThroughputProperties Microsoft.Azure.Cosmos.ThroughputResponse.op_Implicit(Microsoft.Azure.Cosmos.ThroughputResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.ThroughputProperties Resource": { "Type": "Property", @@ -12349,7 +12473,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_FilterPredicate(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -12390,7 +12514,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_EnableContentResponseOnWrite(System.Nullable`1[System.Boolean])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -12542,7 +12666,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_FilterPredicate(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -12582,7 +12706,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" }, "Void set_ConsistencyLevel(System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel])": { "Type": "Method", @@ -12748,7 +12872,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} @@ -12773,7 +12897,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} @@ -12844,7 +12968,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_ts\")]": { + "System.Nullable`1[System.DateTime] LastModified[Newtonsoft.Json.JsonConverterAttribute(typeof(Microsoft.Azure.Documents.UnixDateTimeConverter))]-[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_ts\")]": { "Type": "Property", "Attributes": [ "JsonConverterAttribute", @@ -12852,7 +12976,7 @@ ], "MethodInfo": "System.Nullable`1[System.DateTime] LastModified;CanRead:True;CanWrite:True;System.Nullable`1[System.DateTime] get_LastModified();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_etag\")]": { + "System.String ETag[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_etag\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -12885,7 +13009,7 @@ ], "MethodInfo": "System.String Id;CanRead:True;CanWrite:True;System.String get_Id();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Id(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"_self\")]": { + "System.String SelfLink[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"_self\")]": { "Type": "Property", "Attributes": [ "JsonPropertyAttribute" @@ -12895,7 +13019,7 @@ "Void .ctor(System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String), Void .ctor(System.String)]" + "MethodInfo": "Void .ctor(System.String)" }, "Void set_Id(System.String)": { "Type": "Method", @@ -12949,10 +13073,10 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.User get_User();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Microsoft.Azure.Cosmos.User op_Implicit(Microsoft.Azure.Cosmos.UserResponse)": { + "Microsoft.Azure.Cosmos.User Microsoft.Azure.Cosmos.UserResponse.op_Implicit(Microsoft.Azure.Cosmos.UserResponse)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Microsoft.Azure.Cosmos.User op_Implicit(Microsoft.Azure.Cosmos.UserResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.User Microsoft.Azure.Cosmos.UserResponse.op_Implicit(Microsoft.Azure.Cosmos.UserResponse);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Microsoft.Azure.Cosmos.User User": { "Type": "Property", @@ -13014,6 +13138,13 @@ "Attributes": [], "MethodInfo": "Int32 value__;IsInitOnly:False;IsStatic:False;" }, + "Microsoft.Azure.Cosmos.VectorDataType Float16[System.Runtime.Serialization.EnumMemberAttribute(Value = \"float16\")]": { + "Type": "Field", + "Attributes": [ + "EnumMemberAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.VectorDataType Float16;IsInitOnly:False;IsStatic:True;" + }, "Microsoft.Azure.Cosmos.VectorDataType Float32[System.Runtime.Serialization.EnumMemberAttribute(Value = \"float32\")]": { "Type": "Field", "Attributes": [ @@ -13051,7 +13182,7 @@ "Void .ctor(System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.Embedding])": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.Embedding]), Void .ctor(System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.Embedding])]" + "MethodInfo": "Void .ctor(System.Collections.ObjectModel.Collection`1[Microsoft.Azure.Cosmos.Embedding])" } }, "NestedTypes": {} @@ -13059,6 +13190,18 @@ "Microsoft.Azure.Cosmos.VectorIndexPath;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { + "Int32 get_IndexingSearchListSize()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 get_IndexingSearchListSize();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 IndexingSearchListSize[Newtonsoft.Json.JsonIgnoreAttribute()]": { + "Type": "Property", + "Attributes": [ + "JsonIgnoreAttribute" + ], + "MethodInfo": "Int32 IndexingSearchListSize;CanRead:True;CanWrite:True;Int32 get_IndexingSearchListSize();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_IndexingSearchListSize(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Microsoft.Azure.Cosmos.VectorIndexType get_Type()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -13088,10 +13231,29 @@ ], "MethodInfo": "System.String Path;CanRead:True;CanWrite:True;System.String get_Path();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Path(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "System.String[] get_VectorIndexShardKey()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String[] get_VectorIndexShardKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String[] VectorIndexShardKey[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"vectorIndexShardKey\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String[] VectorIndexShardKey;CanRead:True;CanWrite:True;System.String[] get_VectorIndexShardKey();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_VectorIndexShardKey(System.String[]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" + }, + "Void set_IndexingSearchListSize(Int32)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void set_IndexingSearchListSize(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Void set_Path(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", @@ -13106,6 +13268,13 @@ "CompilerGeneratedAttribute" ], "MethodInfo": "Void set_Type(Microsoft.Azure.Cosmos.VectorIndexType);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_VectorIndexShardKey(System.String[])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_VectorIndexShardKey(System.String[]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -13144,25 +13313,25 @@ } }, "Members": { - "Boolean Equals(System.Object, System.Object)": { + "Boolean Equals(System.Object)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Boolean Equals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean Equals(System.Object)": { + "Int32 GetHashCode()": { "Type": "Method", "Attributes": [], - "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Boolean ReferenceEquals(System.Object, System.Object)": { + "System.Boolean System.Object.Equals(System.Object, System.Object)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Boolean ReferenceEquals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean System.Object.Equals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Int32 GetHashCode()": { + "System.Boolean System.Object.ReferenceEquals(System.Object, System.Object)": { "Type": "Method", "Attributes": [], - "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Boolean System.Object.ReferenceEquals(System.Object, System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "System.String ToString()": { "Type": "Method", @@ -13180,7 +13349,7 @@ "Void .ctor()": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(), Void .ctor()]" + "MethodInfo": "Void .ctor()" } }, "NestedTypes": {} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKTelemetryAPI.net6.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKTelemetryAPI.net6.json new file mode 100644 index 0000000000..b9146c5cd5 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKTelemetryAPI.net6.json @@ -0,0 +1,356 @@ +{ + "Subclasses": { + "Microsoft.Azure.Cosmos.Telemetry.Models.CacheRefreshInfo;Microsoft.Azure.Cosmos.Telemetry.Models.OperationInfo;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Boolean Equals(System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetHashCode()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Telemetry.Models.ClientTelemetryProperties;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Void .ctor(System.String, System.String, System.String, System.String, System.String, System.String, System.String, System.String, System.Nullable`1[System.Boolean], System.Collections.Generic.IReadOnlyList`1[System.String], System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.SystemInfo], System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.CacheRefreshInfo], System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.OperationInfo], System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.RequestInfo], System.String)[Newtonsoft.Json.JsonConstructorAttribute()]": { + "Type": "Constructor", + "Attributes": [ + "JsonConstructorAttribute" + ], + "MethodInfo": "Void .ctor(System.String, System.String, System.String, System.String, System.String, System.String, System.String, System.String, System.Nullable`1[System.Boolean], System.Collections.Generic.IReadOnlyList`1[System.String], System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.SystemInfo], System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.CacheRefreshInfo], System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.OperationInfo], System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.RequestInfo], System.String)" + }, + "Void Write(Newtonsoft.Json.JsonWriter)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void Write(Newtonsoft.Json.JsonWriter);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Void .ctor(System.String, System.String, Double, Int64, Int64, Int64, System.Collections.Generic.IReadOnlyDictionary`2[System.Double,System.Double])": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.String, System.String, Double, Int64, Int64, Int64, System.Collections.Generic.IReadOnlyDictionary`2[System.Double,System.Double])" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Telemetry.Models.OperationInfo;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": { + "Microsoft.Azure.Cosmos.Telemetry.Models.CacheRefreshInfo;Microsoft.Azure.Cosmos.Telemetry.Models.OperationInfo;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Boolean Equals(System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetHashCode()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + } + }, + "Members": { + "Boolean Equals(System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 get_SubStatusCode()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Int32 get_SubStatusCode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetHashCode()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 SubStatusCode[Newtonsoft.Json.JsonPropertyAttribute(PropertyName = \"subStatusCode\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "Int32 SubStatusCode;CanRead:True;CanWrite:False;Int32 get_SubStatusCode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.Telemetry.Models.OperationInfo Copy()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Telemetry.Models.OperationInfo Copy();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Int32] get_StatusCode()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[System.Int32] get_StatusCode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Int32] StatusCode[Newtonsoft.Json.JsonPropertyAttribute(PropertyName = \"statusCode\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.Nullable`1[System.Int32] StatusCode;CanRead:True;CanWrite:False;System.Nullable`1[System.Int32] get_StatusCode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(System.String, System.Nullable`1[System.Boolean], System.String, System.String, System.String, System.String, System.String, System.Nullable`1[System.Int32], Int32, Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(System.String, System.Nullable`1[System.Boolean], System.String, System.String, System.String, System.String, System.String, System.Nullable`1[System.Int32], Int32, Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo)" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Telemetry.Models.RequestInfo;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Boolean Equals(System.Object)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Boolean Equals(System.Object);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Double GetP99Latency()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Double GetP99Latency();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Double GetSampleCount()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Double GetSampleCount();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 get_SubStatusCode()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Int32 get_SubStatusCode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetHashCode()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 GetHashCodeForSampler()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Int32 GetHashCodeForSampler();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Int32 SubStatusCode[Newtonsoft.Json.JsonPropertyAttribute(\"subStatusCode\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "Int32 SubStatusCode;CanRead:True;CanWrite:True;Int32 get_SubStatusCode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_SubStatusCode(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo] get_Metrics()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo] get_Metrics();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo] Metrics[Newtonsoft.Json.JsonPropertyAttribute(\"metricInfo\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo] Metrics;CanRead:True;CanWrite:True;System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo] get_Metrics();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Metrics(System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Int32] get_StatusCode()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[System.Int32] get_StatusCode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Int32] StatusCode[Newtonsoft.Json.JsonPropertyAttribute(\"statusCode\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.Nullable`1[System.Int32] StatusCode;CanRead:True;CanWrite:True;System.Nullable`1[System.Int32] get_StatusCode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_StatusCode(System.Nullable`1[System.Int32]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String ContainerName[Newtonsoft.Json.JsonPropertyAttribute(\"containerName\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String ContainerName;CanRead:True;CanWrite:True;System.String get_ContainerName();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_ContainerName(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String DatabaseName[Newtonsoft.Json.JsonPropertyAttribute(\"databaseName\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String DatabaseName;CanRead:True;CanWrite:True;System.String get_DatabaseName();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_DatabaseName(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_ContainerName()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_ContainerName();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_DatabaseName()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_DatabaseName();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_Operation()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_Operation();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_Resource()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_Resource();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_Uri()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_Uri();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String Operation[Newtonsoft.Json.JsonPropertyAttribute(\"operation\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String Operation;CanRead:True;CanWrite:True;System.String get_Operation();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Operation(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String Resource[Newtonsoft.Json.JsonPropertyAttribute(\"resource\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String Resource;CanRead:True;CanWrite:True;System.String get_Resource();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Resource(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String ToString()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.String ToString();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String Uri[Newtonsoft.Json.JsonPropertyAttribute(\"uri\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String Uri;CanRead:True;CanWrite:True;System.String get_Uri();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_Uri(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor()": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor()" + }, + "Void set_ContainerName(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_ContainerName(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_DatabaseName(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_DatabaseName(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_Metrics(System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_Metrics(System.Collections.Generic.List`1[Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_Operation(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_Operation(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_Resource(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_Resource(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_StatusCode(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_StatusCode(System.Nullable`1[System.Int32]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_SubStatusCode(Int32)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_SubStatusCode(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_Uri(System.String)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_Uri(System.String);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Telemetry.Models.SystemInfo;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Void .ctor(Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "Void .ctor(Microsoft.Azure.Cosmos.Telemetry.Models.MetricInfo)" + } + }, + "NestedTypes": {} + } + }, + "Members": { + "System.Object MemberwiseClone()[System.Runtime.CompilerServices.NullableContextAttribute((Byte)1)]-[System.Runtime.CompilerServices.IntrinsicAttribute()]": { + "Type": "Method", + "Attributes": [ + "IntrinsicAttribute", + "NullableContextAttribute" + ], + "MethodInfo": "System.Object MemberwiseClone();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void Finalize()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void Finalize();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosAuthorizationTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosAuthorizationTests.cs index 71099d9128..b98c339402 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosAuthorizationTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosAuthorizationTests.cs @@ -291,6 +291,57 @@ public async Task TestTokenCredentialErrorAsync() } } + [TestMethod] + [TestCategory("Flaky")] + [Timeout(30000)] + public async Task TestTokenCredentialBackgroundRefreshAsync() + { + // When token is within tokenCredentialRefreshBuffer of expiry, start background task to refresh token, + // but return the cached token. + string token1 = "Token1"; + string token2 = "Token2"; + bool firstTimeGetToken = true; + + TestTokenCredential testTokenCredential = new TestTokenCredential(() => + { + if (firstTimeGetToken) + { + firstTimeGetToken = false; + + return new ValueTask(new AccessToken(token1, DateTimeOffset.UtcNow + TimeSpan.FromSeconds(10))); + } + else + { + return new ValueTask(new AccessToken(token2, DateTimeOffset.MaxValue)); + } + }); + + using (TokenCredentialCache tokenCredentialCache = this.CreateTokenCredentialCache(testTokenCredential)) + { + string t1 = await tokenCredentialCache.GetTokenAsync(NoOpTrace.Singleton); + Assert.AreEqual(token1, t1); + + // Token is valid for 6 seconds. Client TokenCredentialRefreshBuffer is set to 5 seconds. + // After waiting for 2 seconds, the cache token is still valid, but it will be refreshed in the background. + await Task.Delay(TimeSpan.FromSeconds(2)); + + string t2 = await tokenCredentialCache.GetTokenAsync(NoOpTrace.Singleton); + Assert.AreEqual(token1, t2); + + // Wait until the background refresh occurs. + while (testTokenCredential.NumTimesInvoked == 1) + { + await Task.Delay(500); + } + + string t3 = await tokenCredentialCache.GetTokenAsync(NoOpTrace.Singleton); + Assert.AreEqual(token2, t3); + + Assert.AreEqual(2, testTokenCredential.NumTimesInvoked); + this.ValidateSemaphoreIsReleased(tokenCredentialCache); + } + } + // When disposing, the internal cancellationtoken should be signaled [TestMethod] [Timeout(30000)] diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosBadReplicaTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosBadReplicaTests.cs index 16a77da736..42ab0f72cc 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosBadReplicaTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosBadReplicaTests.cs @@ -21,7 +21,6 @@ public class CosmosBadReplicaTests { [TestMethod] [TestCategory("Flaky")] - [Ignore("This test is flaky and is ignored for msdata/direct")] [Timeout(30000)] [DataRow(true, true, false, DisplayName = "Validate when replica validation is enabled using environment variable.")] [DataRow(false, true, false, DisplayName = "Validate when replica validation is disabled using environment variable.")] @@ -201,13 +200,13 @@ public async Task TestGoneFromServiceScenarioAsync( await Task.Delay(TimeSpan.FromMilliseconds(100)); } while (!delayRefreshUnblocked); - for (int i = 0; i < 20; i++) + for (int i = 0; i < 30; i++) { ResponseMessage response = await container.ReadItemStreamAsync(Guid.NewGuid().ToString(), new Cosmos.PartitionKey(Guid.NewGuid().ToString())); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); } - Assert.AreEqual(4, urisVisited.ToHashSet().Count()); + Assert.AreEqual(4, urisVisited.ToHashSet().Count(), "All 4 replicas should be visited after refresh."); // Clears all the setups. No network calls should be done on the next operation. mockHttpHandler.Reset(); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs index fc2108e9b5..b133a10593 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs @@ -26,7 +26,7 @@ namespace Microsoft.Azure.Cosmos.Tests public class CosmosClientOptionsUnitTests { public const string AccountEndpoint = "https://localhost:8081/"; - public const string ConnectionString = "AccountEndpoint=https://localtestcosmos.documents.azure.com:443/;AccountKey=425Mcv8CXQqzRNCgFNjIhT424GK99CKJvASowTnq15Vt8LeahXTcN5wt3342vQ==;"; + public const string ConnectionString = "AccountEndpoint=https://example.documents.azure.com:443/;AccountKey=NotRealKey==;"; public Func HttpClientFactoryDelegate = () => new HttpClient(); [TestMethod] @@ -545,7 +545,7 @@ public void UserAgentContainsPPAFInformation( EnablePartitionLevelFailover = ppaf }; - CosmosClient cosmosClient = cosmosClientBuilder.Build(new MockDocumentClient(policy)); + CosmosClient cosmosClient = cosmosClientBuilder.Build(new MockDocumentClient(policy, thinClient)); CosmosClientOptions cosmosClientOptions = cosmosClient.ClientOptions; @@ -707,7 +707,7 @@ public void ThrowOnNullConnectionString() [ExpectedException(typeof(ArgumentException))] public void ThrowOnMissingAccountKeyInConnectionString() { - string invalidConnectionString = "AccountEndpoint=https://localtestcosmos.documents.azure.com:443/;"; + string invalidConnectionString = "AccountEndpoint=https://example.documents.azure.com:443/;"; new CosmosClientBuilder(invalidConnectionString); } @@ -715,7 +715,7 @@ public void ThrowOnMissingAccountKeyInConnectionString() [ExpectedException(typeof(ArgumentException))] public void ThrowOnMissingAccountEndpointInConnectionString() { - string invalidConnectionString = "AccountKey=425Mcv8CXQqzRNCgFNjIhT424GK99CKJvASowTnq15Vt8LeahXTcN5wt3342vQ==;"; + string invalidConnectionString = "AccountKey=NotRealKey==;"; new CosmosClientBuilder(invalidConnectionString); } @@ -760,7 +760,7 @@ public void VerifyHttpClientFactoryBlockedWithConnectionLimit() public void VerifyHttpClientHandlerIsSet() { string endpoint = AccountEndpoint; - string key = "425Mcv8CXQqzRNCgFNjIhT424GK99CKJvASowTnq15Vt8LeahXTcN5wt3342vQ=="; + string key = "NotRealKey=="; IWebProxy webProxy = new TestWebProxy(); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientTests.cs index fb23dce2c4..f721cb066e 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientTests.cs @@ -29,7 +29,7 @@ namespace Microsoft.Azure.Cosmos.Tests public class CosmosClientTests { public const string AccountEndpoint = "https://localhost:8081/"; - public const string ConnectionString = "AccountEndpoint=https://localtestcosmos.documents.azure.com:443/;AccountKey=425Mcv8CXQqzRNCgFNjIhT424GK99CKJvASowTnq15Vt8LeahXTcN5wt3342vQ==;"; + public const string ConnectionString = "AccountEndpoint=https://example.documents.azure.com:443/;AccountKey=NotRealKey==;"; [TestMethod] public async Task TestDispose() @@ -74,7 +74,7 @@ public async Task TestDispose() catch (CosmosObjectDisposedException e) { string expectedMessage = $"Cannot access a disposed 'CosmosClient'. Follow best practices and use the CosmosClient as a singleton." + - $" CosmosClient was disposed at: {cosmosClient.DisposedDateTimeUtc.Value.ToString("o", CultureInfo.InvariantCulture)}; CosmosClient Endpoint: https://localtestcosmos.documents.azure.com/; Created at: {cosmosClient.ClientConfigurationTraceDatum.ClientCreatedDateTimeUtc.ToString("o", CultureInfo.InvariantCulture)}; UserAgent: {userAgent};"; + $" CosmosClient was disposed at: {cosmosClient.DisposedDateTimeUtc.Value.ToString("o", CultureInfo.InvariantCulture)}; CosmosClient Endpoint: https://example.documents.azure.com/; Created at: {cosmosClient.ClientConfigurationTraceDatum.ClientCreatedDateTimeUtc.ToString("o", CultureInfo.InvariantCulture)}; UserAgent: {userAgent};"; Assert.IsTrue(e.Message.Contains(expectedMessage)); string diagnostics = e.Diagnostics.ToString(); Assert.IsNotNull(diagnostics); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosContainerSettingsTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosContainerSettingsTests.cs index 65f14d567c..d4d9f711a2 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosContainerSettingsTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosContainerSettingsTests.cs @@ -212,11 +212,11 @@ public void ValidateVectorEmbeddingsAndIndexes() }; Collection embeddings = new Collection() - { - embedding1, - embedding2, - embedding3, - }; + { + embedding1, + embedding2, + embedding3, + }; ContainerProperties containerSettings = new ContainerProperties(id: "TestContainer", partitionKeyPath: "/partitionKey") { @@ -224,28 +224,30 @@ public void ValidateVectorEmbeddingsAndIndexes() IndexingPolicy = new Cosmos.IndexingPolicy() { VectorIndexes = new() - { - new Cosmos.VectorIndexPath() - { - Path = "/vector1", - Type = Cosmos.VectorIndexType.Flat, - }, - new Cosmos.VectorIndexPath() - { - Path = "/vector2", - Type = Cosmos.VectorIndexType.QuantizedFlat, - VectorIndexShardKey = new[] { "/Country" }, - QuantizationByteSize = 3, - }, - new Cosmos.VectorIndexPath() - { - Path = "/vector3", - Type = Cosmos.VectorIndexType.DiskANN, - VectorIndexShardKey = new[] { "/ZipCode" }, - QuantizationByteSize = 2, - IndexingSearchListSize = 5, - } - }, + { + new Cosmos.VectorIndexPath() + { + Path = "/vector1", + Type = Cosmos.VectorIndexType.Flat, + }, + new Cosmos.VectorIndexPath() + { + Path = "/vector2", + Type = Cosmos.VectorIndexType.QuantizedFlat, + QuantizerType = Cosmos.QuantizerType.Product, + VectorIndexShardKey = new[] { "/Country" }, + QuantizationByteSize = 3, + }, + new Cosmos.VectorIndexPath() + { + Path = "/vector3", + Type = Cosmos.VectorIndexType.DiskANN, + QuantizerType = Cosmos.QuantizerType.Spherical, + VectorIndexShardKey = new[] { "/ZipCode" }, + QuantizationByteSize = 2, + IndexingSearchListSize = 5, + } + }, }, }; @@ -261,18 +263,99 @@ public void ValidateVectorEmbeddingsAndIndexes() Collection vectorIndexes = containerSettings.IndexingPolicy.VectorIndexes; Assert.AreEqual("/vector1", vectorIndexes[0].Path); Assert.AreEqual(Cosmos.VectorIndexType.Flat, vectorIndexes[0].Type); + Assert.IsNull(vectorIndexes[0].QuantizerType); // Flat type doesn't use quantizer + Assert.AreEqual("/vector2", vectorIndexes[1].Path); Assert.AreEqual(Cosmos.VectorIndexType.QuantizedFlat, vectorIndexes[1].Type); + Assert.AreEqual(Cosmos.QuantizerType.Product, vectorIndexes[1].QuantizerType); Assert.AreEqual(3, vectorIndexes[1].QuantizationByteSize); CollectionAssert.AreEqual(new string[] { "/Country" }, vectorIndexes[1].VectorIndexShardKey); Assert.AreEqual("/vector3", vectorIndexes[2].Path); Assert.AreEqual(Cosmos.VectorIndexType.DiskANN, vectorIndexes[2].Type); + Assert.AreEqual(Cosmos.QuantizerType.Spherical, vectorIndexes[2].QuantizerType); Assert.AreEqual(2, vectorIndexes[2].QuantizationByteSize); Assert.AreEqual(5, vectorIndexes[2].IndexingSearchListSize); CollectionAssert.AreEqual(new string[] { "/ZipCode" }, vectorIndexes[2].VectorIndexShardKey); } + [TestMethod] + public void ValidateVectorIndexQuantizerTypeSerialization() + { + // Test with Product quantizer + Cosmos.VectorIndexPath vectorIndexProduct = new Cosmos.VectorIndexPath() + { + Path = "/vector", + Type = Cosmos.VectorIndexType.DiskANN, + QuantizerType = Cosmos.QuantizerType.Product, + QuantizationByteSize = 2, + IndexingSearchListSize = 100 + }; + + // Serialize + string serializedProduct = JsonConvert.SerializeObject(vectorIndexProduct); + + // Verify the JSON contains quantizerType + Assert.IsTrue(serializedProduct.Contains("\"quantizerType\":\"product\"")); + + // Deserialize + Cosmos.VectorIndexPath deserializedProduct = JsonConvert.DeserializeObject(serializedProduct); + + // Verify round-trip + Assert.AreEqual(vectorIndexProduct.Path, deserializedProduct.Path); + Assert.AreEqual(vectorIndexProduct.Type, deserializedProduct.Type); + Assert.AreEqual(Cosmos.QuantizerType.Product, deserializedProduct.QuantizerType); + Assert.AreEqual(vectorIndexProduct.QuantizationByteSize, deserializedProduct.QuantizationByteSize); + Assert.AreEqual(vectorIndexProduct.IndexingSearchListSize, deserializedProduct.IndexingSearchListSize); + + // Test with Spherical quantizer + Cosmos.VectorIndexPath vectorIndexSpherical = new Cosmos.VectorIndexPath() + { + Path = "/embedding", + Type = Cosmos.VectorIndexType.QuantizedFlat, + QuantizerType = Cosmos.QuantizerType.Spherical, + QuantizationByteSize = 3, + VectorIndexShardKey = new[] { "/region" } + }; + + // Serialize + string serializedSpherical = JsonConvert.SerializeObject(vectorIndexSpherical); + + // Verify the JSON contains quantizerType + Assert.IsTrue(serializedSpherical.Contains("\"quantizerType\":\"spherical\"")); + + // Deserialize + Cosmos.VectorIndexPath deserializedSpherical = JsonConvert.DeserializeObject(serializedSpherical); + + // Verify round-trip + Assert.AreEqual(vectorIndexSpherical.Path, deserializedSpherical.Path); + Assert.AreEqual(vectorIndexSpherical.Type, deserializedSpherical.Type); + Assert.AreEqual(Cosmos.QuantizerType.Spherical, deserializedSpherical.QuantizerType); + Assert.AreEqual(vectorIndexSpherical.QuantizationByteSize, deserializedSpherical.QuantizationByteSize); + CollectionAssert.AreEqual(vectorIndexSpherical.VectorIndexShardKey, deserializedSpherical.VectorIndexShardKey); + + // Test with null quantizer type (for Flat index) + Cosmos.VectorIndexPath vectorIndexFlat = new Cosmos.VectorIndexPath() + { + Path = "/flatVector", + Type = Cosmos.VectorIndexType.Flat + }; + + // Serialize + string serializedFlat = JsonConvert.SerializeObject(vectorIndexFlat); + + // Verify the JSON doesn't contain quantizerType (null value handling) + Assert.IsFalse(serializedFlat.Contains("quantizerType")); + + // Deserialize + Cosmos.VectorIndexPath deserializedFlat = JsonConvert.DeserializeObject(serializedFlat); + + // Verify round-trip + Assert.AreEqual(vectorIndexFlat.Path, deserializedFlat.Path); + Assert.AreEqual(vectorIndexFlat.Type, deserializedFlat.Type); + Assert.IsNull(deserializedFlat.QuantizerType); + } + [TestMethod] public void ValidateFullTextPathsAndIndexes() { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosDiagnosticsUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosDiagnosticsUnitTests.cs index 404955997d..5dcee22a47 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosDiagnosticsUnitTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosDiagnosticsUnitTests.cs @@ -12,6 +12,7 @@ namespace Microsoft.Azure.Cosmos.Tests using System.Net.Http; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Diagnostics; using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Cosmos.Tracing.TraceData; using Microsoft.Azure.Documents; @@ -68,6 +69,11 @@ public void ValidateTransportHandlerLogging() Assert.AreEqual(HttpStatusCode.Gone, response.StatusCode); Assert.AreEqual(SubStatusCodes.PartitionKeyRangeGone, response.Headers.SubStatusCode); + if (trace is Cosmos.Tracing.Trace rootLevelTrace) + { + rootLevelTrace.SetWalkingStateRecursively(); + } + IEnumerable pointOperationStatistics = trace.Data.Values .Where(traceDatum => traceDatum is PointOperationStatisticsTraceDatum operationStatistics) .Select(x => (PointOperationStatisticsTraceDatum)x); @@ -137,6 +143,78 @@ public async Task ValidateActivityIdWithSynchronizationContext() } } + [TestMethod] + public void ValidateConcurrentToStringCalls() + { + // Create a trace and simulate concurrent access while calling ToString() + using ITrace trace = Microsoft.Azure.Cosmos.Tracing.Trace.GetRootTrace("concurrency-test"); + + Exception caughtException = null; + const int numThreads = 10; + const int numIterations = 100; + using CountdownEvent countdown = new CountdownEvent(numThreads); + + // Start multiple threads that will concurrently modify the trace + for (int i = 0; i < numThreads; i++) + { + int threadId = i; + Task.Run(() => + { + try + { + for (int j = 0; j < numIterations; j++) + { + // Add children and data concurrently + using ITrace child = trace.StartChild($"child-{threadId}-{j}"); + child.AddDatum($"key-{threadId}-{j}", $"value-{threadId}-{j}"); + + // Simulate some work + Thread.Sleep(1); + } + } + catch (Exception ex) + { + Interlocked.CompareExchange(ref caughtException, ex, null); + } + finally + { + countdown.Signal(); + } + }); + } + + // Concurrently call ToString() which caused the original issue + Task toStringTask = Task.Run(() => + { + try + { + for (int i = 0; i < numIterations; i++) + { + CosmosTraceDiagnostics diagnostics = new CosmosTraceDiagnostics(trace); + string diagnosticsString = diagnostics.ToString(); + Assert.IsNotNull(diagnosticsString); + + // Simulate some work + Thread.Sleep(1); + } + } + catch (Exception ex) + { + Interlocked.CompareExchange(ref caughtException, ex, null); + } + }); + + // Wait for all threads to complete + countdown.Wait(TimeSpan.FromSeconds(30)); + toStringTask.Wait(TimeSpan.FromSeconds(30)); + + // Verify no exceptions occurred + if (caughtException != null) + { + Assert.Fail($"Concurrent access caused exception: {caughtException}"); + } + } + private Task ValidateActivityIdHelper() { Guid activityId = System.Diagnostics.Trace.CorrelationManager.ActivityId; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs index 35cde9017d..8ecd4f30b8 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs @@ -457,6 +457,108 @@ public void GenerateEventWithDiagnosticWithFilterOnStatusCodeTest() } } + /// + /// Validates that substatus code 1003 (OwnerResourceNotFound) can be used to distinguish + /// between a regular 404 (item not found) and a 404 where the parent resource doesn't exist. + /// + [TestMethod] + public void ValidateSubStatusCode1003ForOwnerResourceNotFound() + { + // Test creating a NotFoundException with substatus code 1003 + string testMessage = "Owner resource not found"; + string activityId = Guid.NewGuid().ToString(); + int ownerNotFoundSubStatus = 1003; // SubStatusCodes.OwnerResourceNotFound + double requestCharge = 1.0; + + CosmosException exception = CosmosExceptionFactory.CreateNotFoundException( + testMessage, + new Headers() + { + SubStatusCodeLiteral = ownerNotFoundSubStatus.ToString(), + ActivityId = activityId, + RequestCharge = requestCharge + }); + + Assert.AreEqual(HttpStatusCode.NotFound, exception.StatusCode); + Assert.AreEqual(ownerNotFoundSubStatus, exception.SubStatusCode); + Assert.AreEqual(ownerNotFoundSubStatus.ToString(), exception.Headers.SubStatusCodeLiteral); + Assert.AreEqual(testMessage, exception.ResponseBody); + Assert.AreEqual(activityId, exception.ActivityId); + Assert.AreEqual(requestCharge, exception.RequestCharge); + + // Verify the exception message contains all the relevant information + Assert.IsTrue(exception.Message.Contains("404")); + Assert.IsTrue(exception.Message.Contains("1003")); + Assert.IsTrue(exception.ToString().Contains(testMessage)); + } + + /// + /// Validates that substatus code 0 distinguishes a regular item not found from owner not found. + /// + [TestMethod] + public void ValidateSubStatusCode0ForRegularItemNotFound() + { + // Test creating a NotFoundException with substatus code 0 (regular item not found) + string testMessage = "Item not found"; + string activityId = Guid.NewGuid().ToString(); + int itemNotFoundSubStatus = 0; + double requestCharge = 1.0; + + CosmosException exception = CosmosExceptionFactory.CreateNotFoundException( + testMessage, + new Headers() + { + SubStatusCodeLiteral = itemNotFoundSubStatus.ToString(), + ActivityId = activityId, + RequestCharge = requestCharge + }); + + Assert.AreEqual(HttpStatusCode.NotFound, exception.StatusCode); + Assert.AreEqual(itemNotFoundSubStatus, exception.SubStatusCode); + Assert.AreEqual(itemNotFoundSubStatus.ToString(), exception.Headers.SubStatusCodeLiteral); + Assert.AreEqual(testMessage, exception.ResponseBody); + Assert.AreEqual(activityId, exception.ActivityId); + Assert.AreEqual(requestCharge, exception.RequestCharge); + + // Verify the exception message contains all the relevant information + Assert.IsTrue(exception.Message.Contains("404")); + Assert.IsTrue(exception.Message.Contains("0")); + Assert.IsTrue(exception.ToString().Contains(testMessage)); + } + + /// + /// Validates that ResponseMessage correctly exposes substatus codes for 404 errors. + /// + [TestMethod] + public void ValidateResponseMessageSubStatusCodeForNotFound() + { + // Create ResponseMessage with 404 and substatus 0 (item not found) + Headers headersItemNotFound = new Headers() + { + SubStatusCodeLiteral = "0", + ActivityId = Guid.NewGuid().ToString() + }; + + ResponseMessage responseItemNotFound = new ResponseMessage(HttpStatusCode.NotFound) + { + Headers = { } + }; + responseItemNotFound.Headers.SubStatusCodeLiteral = "0"; + + Assert.AreEqual(HttpStatusCode.NotFound, responseItemNotFound.StatusCode); + Assert.AreEqual(0, (int)responseItemNotFound.Headers.SubStatusCode); + + // Create ResponseMessage with 404 and substatus 1003 (owner resource not found) + ResponseMessage responseOwnerNotFound = new ResponseMessage(HttpStatusCode.NotFound) + { + Headers = { } + }; + responseOwnerNotFound.Headers.SubStatusCodeLiteral = "1003"; + + Assert.AreEqual(HttpStatusCode.NotFound, responseOwnerNotFound.StatusCode); + Assert.AreEqual(1003, (int)responseOwnerNotFound.Headers.SubStatusCode); + } + private void ValidateExceptionInfo( CosmosException exception, HttpStatusCode httpStatusCode, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosHttpClientCoreTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosHttpClientCoreTests.cs index 00980726ba..4d4eb201a2 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosHttpClientCoreTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosHttpClientCoreTests.cs @@ -57,7 +57,6 @@ static Task sendFunc(HttpRequestMessage request, Cancellati [TestMethod] [TestCategory("Flaky")] - [Ignore] public async Task RetryTransientIssuesTestAsync() { using CancellationTokenSource cancellationTokenSource1 = new CancellationTokenSource(); @@ -124,7 +123,8 @@ async Task sendFunc(HttpRequestMessage request, Cancellatio resourceType: ResourceType.Collection, timeoutPolicy: currentTimeoutPolicy.Key, clientSideRequestStatistics: new ClientSideRequestStatisticsTraceDatum(DateTime.UtcNow, trace), - cancellationToken: default); + cancellationToken: default, + documentServiceRequest: CreateDocumentServiceRequestByOperation(ResourceType.Collection, OperationType.Read)); Assert.AreEqual(HttpStatusCode.OK, responseMessage.StatusCode); } @@ -267,7 +267,8 @@ Task sendFunc(HttpRequestMessage request, CancellationToken resourceType: ResourceType.Collection, timeoutPolicy: HttpTimeoutPolicyDefault.Instance, clientSideRequestStatistics: new ClientSideRequestStatisticsTraceDatum(DateTime.UtcNow, trace), - cancellationToken: default); + cancellationToken: default, + documentServiceRequest: CreateDocumentServiceRequestByOperation(ResourceType.Collection, OperationType.Read)); } } catch (Exception) @@ -282,7 +283,7 @@ Task sendFunc(HttpRequestMessage request, CancellationToken public async Task HttpTimeoutThrow503TestAsync() { - async Task TestScenarioAsync(HttpMethod method, ResourceType resourceType, HttpTimeoutPolicy timeoutPolicy, Type expectedException, int expectedNumberOfRetrys) + async Task TestScenarioAsync(HttpMethod method, ResourceType resourceType, OperationType operationType, HttpTimeoutPolicy timeoutPolicy, Type expectedException, int expectedNumberOfRetrys) { int count = 0; Task sendFunc(HttpRequestMessage request, CancellationToken cancellationToken) @@ -307,7 +308,8 @@ Task sendFunc(HttpRequestMessage request, CancellationToken resourceType: resourceType, timeoutPolicy: timeoutPolicy, clientSideRequestStatistics: new ClientSideRequestStatisticsTraceDatum(DateTime.UtcNow, trace), - cancellationToken: default); + cancellationToken: default, + documentServiceRequest: CreateDocumentServiceRequestByOperation(resourceType, operationType)); } } catch (Exception e) @@ -329,19 +331,19 @@ Task sendFunc(HttpRequestMessage request, CancellationToken } //Data plane read - await TestScenarioAsync(HttpMethod.Get, ResourceType.Document, HttpTimeoutPolicyDefault.InstanceShouldThrow503OnTimeout, typeof(CosmosException), 3); + await TestScenarioAsync(HttpMethod.Get, ResourceType.Document, OperationType.Read, HttpTimeoutPolicyDefault.InstanceShouldThrow503OnTimeout, typeof(CosmosException), 3); //Data plane write (Should throw a 408 OperationCanceledException rather than a 503) - await TestScenarioAsync(HttpMethod.Post, ResourceType.Document, HttpTimeoutPolicyDefault.Instance, typeof(TaskCanceledException), 1); + await TestScenarioAsync(HttpMethod.Post, ResourceType.Document, OperationType.Upsert, HttpTimeoutPolicyDefault.Instance, typeof(TaskCanceledException), 1); //Meta data read - await TestScenarioAsync(HttpMethod.Get, ResourceType.Database, HttpTimeoutPolicyDefault.InstanceShouldThrow503OnTimeout, typeof(CosmosException), 3); + await TestScenarioAsync(HttpMethod.Get, ResourceType.Database, OperationType.Read, HttpTimeoutPolicyDefault.InstanceShouldThrow503OnTimeout, typeof(CosmosException), 3); //Query plan read (note all query plan operations are reads). - await TestScenarioAsync(HttpMethod.Get, ResourceType.Document, HttpTimeoutPolicyDefault.InstanceShouldThrow503OnTimeout, typeof(CosmosException), 3); + await TestScenarioAsync(HttpMethod.Get, ResourceType.Document, OperationType.Read, HttpTimeoutPolicyDefault.InstanceShouldThrow503OnTimeout, typeof(CosmosException), 3); //Metadata Write (Should throw a 408 OperationCanceledException rather than a 503) - await TestScenarioAsync(HttpMethod.Post, ResourceType.Document, HttpTimeoutPolicyDefault.Instance, typeof(TaskCanceledException), 1); + await TestScenarioAsync(HttpMethod.Post, ResourceType.Document, OperationType.Upsert, HttpTimeoutPolicyDefault.Instance, typeof(TaskCanceledException), 1); } [TestMethod] @@ -384,6 +386,63 @@ Task sendFunc(HttpRequestMessage request, CancellationToken Assert.AreEqual(1, count, "Should not retry at all"); } + [TestMethod] + [TestCategory("Flaky")] + public async Task RetryTransientIssuesForQueryPlanTestAsync() + { + DocumentServiceRequest documentServiceRequest = DocumentServiceRequest.Create( + OperationType.QueryPlan, + ResourceType.Document, + @"dbs/1889fcb0-7d02-41a4-94c9-189f6aa1b444/colls/c264ae0f-7708-46fb-a015-29a40ea3c18b", + new MemoryStream(), + AuthorizationTokenType.PrimaryMasterKey, + new Documents.Collections.RequestNameValueCollection()); + + HttpTimeoutPolicy retryPolicy = HttpTimeoutPolicy.GetTimeoutPolicy(documentServiceRequest); + Assert.AreEqual(HttpTimeoutPolicyControlPlaneRetriableHotPath.InstanceShouldThrow503OnTimeout, retryPolicy); + + int count = 0; + IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> retry = retryPolicy.GetTimeoutEnumerator(); + async Task sendFunc(HttpRequestMessage request, CancellationToken cancellationToken) + { + count++; + retry.MoveNext(); + + if (count <= 2) + { + Assert.IsFalse(cancellationToken.IsCancellationRequested); + await Task.Delay(retry.Current.requestTimeout + TimeSpan.FromSeconds(.1)); + cancellationToken.ThrowIfCancellationRequested(); + Assert.Fail("Cancellation token should be canceled"); + } + + if (count == 3) + { + return new HttpResponseMessage(HttpStatusCode.OK); + } + + throw new Exception("Should not return after the success"); + } + + DocumentClientEventSource eventSource = DocumentClientEventSource.Instance; + HttpMessageHandler messageHandler = new MockMessageHandler(sendFunc); + using CosmosHttpClient cosmoshttpClient = MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler)); + + using (ITrace trace = Trace.GetRootTrace(nameof(RetryTransientIssuesForQueryPlanTestAsync))) + { + HttpResponseMessage responseMessage = await cosmoshttpClient.SendHttpAsync(() => + new ValueTask( + result: new HttpRequestMessage(HttpMethod.Post, new Uri("http://localhost"))), + resourceType: ResourceType.Document, + timeoutPolicy: HttpTimeoutPolicyControlPlaneRetriableHotPath.Instance, + clientSideRequestStatistics: new ClientSideRequestStatisticsTraceDatum(DateTime.UtcNow, trace), + cancellationToken: default, + documentServiceRequest: documentServiceRequest); + + Assert.AreEqual(HttpStatusCode.OK, responseMessage.StatusCode); + } + } + [TestMethod] public void CreateSocketsHttpHandlerCreatesCorrectValueType() { @@ -437,7 +496,7 @@ public void CreateHttpClientHandlerCreatesCorrectValueType() public async Task HttpTimeoutPolicyForThinClientOn503TestAsync() { - async Task TestScenarioAsync(HttpMethod method, ResourceType resourceType, HttpTimeoutPolicy timeoutPolicy, Type expectedException, int expectedNumberOfRetrys) + async Task TestScenarioAsync(HttpMethod method, ResourceType resourceType, OperationType operationType, HttpTimeoutPolicy timeoutPolicy, Type expectedException, int expectedNumberOfRetrys) { int count = 0; Task sendFunc(HttpRequestMessage request, CancellationToken cancellationToken) @@ -462,7 +521,8 @@ Task sendFunc(HttpRequestMessage request, CancellationToken resourceType: resourceType, timeoutPolicy: timeoutPolicy, clientSideRequestStatistics: new ClientSideRequestStatisticsTraceDatum(DateTime.UtcNow, trace), - cancellationToken: default); + cancellationToken: default, + documentServiceRequest: CreateDocumentServiceRequestByOperation(resourceType, operationType)); } } catch (Exception e) @@ -487,6 +547,7 @@ Task sendFunc(HttpRequestMessage request, CancellationToken await TestScenarioAsync( method: HttpMethod.Get, resourceType: ResourceType.Document, + operationType: OperationType.Read, timeoutPolicy: HttpTimeoutPolicy.GetTimeoutPolicy( documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Document, OperationType.Read), isPartitionLevelFailoverEnabled: false, @@ -498,6 +559,7 @@ await TestScenarioAsync( await TestScenarioAsync( method: HttpMethod.Get, resourceType: ResourceType.Document, + operationType: OperationType.Read, timeoutPolicy: HttpTimeoutPolicy.GetTimeoutPolicy( documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Document, OperationType.Query), isPartitionLevelFailoverEnabled: false, @@ -509,6 +571,7 @@ await TestScenarioAsync( await TestScenarioAsync( method: HttpMethod.Post, resourceType: ResourceType.Document, + operationType: OperationType.Upsert, timeoutPolicy: HttpTimeoutPolicy.GetTimeoutPolicy( documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Document, OperationType.Create), isPartitionLevelFailoverEnabled: false, @@ -520,6 +583,7 @@ await TestScenarioAsync( await TestScenarioAsync( method: HttpMethod.Get, resourceType: ResourceType.Database, + operationType: OperationType.Read, timeoutPolicy: HttpTimeoutPolicy.GetTimeoutPolicy( documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Database, OperationType.Read), isPartitionLevelFailoverEnabled: false, @@ -528,6 +592,102 @@ await TestScenarioAsync( expectedNumberOfRetrys: 3); } + [TestMethod] + public void HttpTimeoutPolicyForParitionFailoverForQueries() + { + HttpTimeoutPolicy httpTimeoutPolicyForQuery = HttpTimeoutPolicy.GetTimeoutPolicy( + documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Document, OperationType.Query), + isPartitionLevelFailoverEnabled: true, + isThinClientEnabled: false); + IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> availableRetries = httpTimeoutPolicyForQuery.GetTimeoutEnumerator(); + + int count = 0; + while (availableRetries.MoveNext()) + { + if (count <= 1) + { + Assert.AreEqual(new TimeSpan(0,0,6), availableRetries.Current.requestTimeout); + } + else if (count == 2) + { + Assert.AreEqual(new TimeSpan(0, 0, 10), availableRetries.Current.requestTimeout); + } + count++; + } + } + + [TestMethod] + public void HttpTimeoutPolicyForParitionFailoverForReads() + { + HttpTimeoutPolicy httpTimeoutPolicyForPointReads = HttpTimeoutPolicy.GetTimeoutPolicy( + documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Document, OperationType.Read), + isPartitionLevelFailoverEnabled: true, + isThinClientEnabled: false); + IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> availableRetries = httpTimeoutPolicyForPointReads.GetTimeoutEnumerator(); + + int count = 0; + while (availableRetries.MoveNext()) + { + if (count <= 1) + { + Assert.AreEqual(new TimeSpan(0, 0, 6), availableRetries.Current.requestTimeout); + } + else if (count == 2) + { + Assert.AreEqual(new TimeSpan(0, 0, 10), availableRetries.Current.requestTimeout); + } + count++; + } + } + + [TestMethod] + public void HttpTimeoutPolicyWhenThinClientEnabledForPointReads() + { + HttpTimeoutPolicy httpTimeoutPolicyForPointReads = HttpTimeoutPolicy.GetTimeoutPolicy( + documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Document, OperationType.Read), + isPartitionLevelFailoverEnabled: false, + isThinClientEnabled: true); + IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> availableRetries = httpTimeoutPolicyForPointReads.GetTimeoutEnumerator(); + + int count = 0; + while (availableRetries.MoveNext()) + { + if (count <= 1) + { + Assert.AreEqual(new TimeSpan(0, 0, 6), availableRetries.Current.requestTimeout); + } + else if (count == 2) + { + Assert.AreEqual(new TimeSpan(0, 0, 10), availableRetries.Current.requestTimeout); + } + count++; + } + } + + [TestMethod] + public void HttpTimeoutPolicyWhenThinClientEnabledForNonPointReads() + { + HttpTimeoutPolicy httpTimeoutPolicyForQuery = HttpTimeoutPolicy.GetTimeoutPolicy( + documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Document, OperationType.Query), + isPartitionLevelFailoverEnabled: false, + isThinClientEnabled: true); + IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> availableRetries = httpTimeoutPolicyForQuery.GetTimeoutEnumerator(); + + int count = 0; + while (availableRetries.MoveNext()) + { + if (count <= 1) + { + Assert.AreEqual(new TimeSpan(0, 0, 6), availableRetries.Current.requestTimeout); + } + else if (count == 2) + { + Assert.AreEqual(new TimeSpan(0, 0, 10), availableRetries.Current.requestTimeout); + } + count++; + } + } + private static DocumentServiceRequest CreateDocumentServiceRequestByOperation( ResourceType resourceType, OperationType operationType) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosItemUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosItemUnitTests.cs index 0f5cc0e13e..7c9291f424 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosItemUnitTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosItemUnitTests.cs @@ -253,6 +253,7 @@ public async Task TestGetPartitionKeyValueFromStreamAsync(bool binaryEncodingEna cancellationToken)); DateTime dateTime = new DateTime(2019, 05, 15, 12, 1, 2, 3, DateTimeKind.Utc); + DateTimeOffset dateTimeOffset = new DateTimeOffset(2019, 05, 15, 12, 1, 2, 3, TimeSpan.Zero); Guid guid = Guid.NewGuid(); //Test supported types @@ -273,6 +274,7 @@ public async Task TestGetPartitionKeyValueFromStreamAsync(bool binaryEncodingEna new { pk = char.MaxValue }, new { pk = "test" }, new { pk = dateTime }, + new { pk = dateTimeOffset}, new { pk = guid }, }; @@ -308,6 +310,10 @@ public async Task TestGetPartitionKeyValueFromStreamAsync(bool binaryEncodingEna { Assert.AreEqual(poco.pk.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), stringValue); } + else if (poco.pk is DateTimeOffset) + { + Assert.AreEqual(poco.pk.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), stringValue); + } else { Assert.AreEqual(poco.pk.ToString(), (string)pk); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosJsonSerializerUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosJsonSerializerUnitTests.cs index 1ebff95eec..f8deb921cd 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosJsonSerializerUnitTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosJsonSerializerUnitTests.cs @@ -55,7 +55,7 @@ public void ValidatePropertySerialization() string id = "testId"; this.TestProperty( id, - $@"{{""id"":""{id}"",""writableLocations"":[],""readableLocations"":[],""userConsistencyPolicy"":null,""addresses"":null,""userReplicationPolicy"":null,""systemReplicationPolicy"":null,""readPolicy"":null,""queryEngineConfiguration"":null,""enableMultipleWriteLocations"":false,""enablePerPartitionFailoverBehavior"":null}}"); + $@"{{""id"":""{id}"",""writableLocations"":[],""readableLocations"":[],""userConsistencyPolicy"":null,""addresses"":null,""userReplicationPolicy"":null,""systemReplicationPolicy"":null,""readPolicy"":null,""queryEngineConfiguration"":null,""enableMultipleWriteLocations"":false,""enablePerPartitionFailoverBehavior"":null,""enableNRegionSynchronousCommit"":false}}"); this.TestProperty( id, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosSerializerCoreTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosSerializerCoreTests.cs index 7b18b25518..00f00156fc 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosSerializerCoreTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosSerializerCoreTests.cs @@ -143,7 +143,7 @@ public void ValidateCustomSerializerNotUsedForInternalTypes() this.TestProperty( serializerCore, id, - $@"{{""id"":""{id}"",""writableLocations"":[],""readableLocations"":[],""userConsistencyPolicy"":null,""addresses"":null,""userReplicationPolicy"":null,""systemReplicationPolicy"":null,""readPolicy"":null,""queryEngineConfiguration"":null,""enableMultipleWriteLocations"":false,""enablePerPartitionFailoverBehavior"":null}}"); + $@"{{""id"":""{id}"",""writableLocations"":[],""readableLocations"":[],""userConsistencyPolicy"":null,""addresses"":null,""userReplicationPolicy"":null,""systemReplicationPolicy"":null,""readPolicy"":null,""queryEngineConfiguration"":null,""enableMultipleWriteLocations"":false,""enablePerPartitionFailoverBehavior"":null,""enableNRegionSynchronousCommit"":false}}"); this.TestProperty( serializerCore, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ExceptionlessTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ExceptionlessTests.cs index b9539ef8c4..17d3113213 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ExceptionlessTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ExceptionlessTests.cs @@ -321,7 +321,8 @@ private static GatewayStoreModel MockGatewayStoreModel(Func new HttpClient(messageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); } private static Mock GetMockAddressCache(AddressInformation[] addressInformation) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Fluent/ContainerDefinitionForCreateTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Fluent/ContainerDefinitionForCreateTests.cs index 7d4c20cc71..91dcd7ae2f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Fluent/ContainerDefinitionForCreateTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Fluent/ContainerDefinitionForCreateTests.cs @@ -543,11 +543,13 @@ public async Task ValidateVectorEmbeddingsAndIndexingPolicyUsingContainerBuilder .Attach() .WithVectorIndex() .Path(vector2Path, VectorIndexType.QuantizedFlat) + .WithQuantizerType(QuantizerType.Product) .WithQuantizationByteSize(3) .WithVectorIndexShardKey(new string[] { "/Country" }) .Attach() .WithVectorIndex() .Path(vector3Path, VectorIndexType.DiskANN) + .WithQuantizerType(QuantizerType.Spherical) .WithQuantizationByteSize(2) .WithIndexingSearchListSize(35) .WithVectorIndexShardKey(new string[] { "/ZipCode" }) @@ -557,7 +559,8 @@ public async Task ValidateVectorEmbeddingsAndIndexingPolicyUsingContainerBuilder Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); mockContainers.Verify(c => c.CreateContainerAsync( - It.Is((settings) => settings.VectorEmbeddingPolicy.Embeddings.Count == 3 + It.Is((settings) => + settings.VectorEmbeddingPolicy.Embeddings.Count == 3 && vector1Path.Equals(settings.VectorEmbeddingPolicy.Embeddings[0].Path) && VectorDataType.Int8.Equals(settings.VectorEmbeddingPolicy.Embeddings[0].DataType) && DistanceFunction.DotProduct.Equals(settings.VectorEmbeddingPolicy.Embeddings[0].DistanceFunction) @@ -569,12 +572,107 @@ public async Task ValidateVectorEmbeddingsAndIndexingPolicyUsingContainerBuilder && vector3Path.Equals(settings.VectorEmbeddingPolicy.Embeddings[2].Path) && VectorDataType.Float32.Equals(settings.VectorEmbeddingPolicy.Embeddings[2].DataType) && DistanceFunction.Euclidean.Equals(settings.VectorEmbeddingPolicy.Embeddings[2].DistanceFunction) - && 400.Equals(settings.VectorEmbeddingPolicy.Embeddings[2].Dimensions)), + && 400.Equals(settings.VectorEmbeddingPolicy.Embeddings[2].Dimensions) + && settings.IndexingPolicy.VectorIndexes.Count == 3 + && vector1Path.Equals(settings.IndexingPolicy.VectorIndexes[0].Path) + && VectorIndexType.Flat.Equals(settings.IndexingPolicy.VectorIndexes[0].Type) + && settings.IndexingPolicy.VectorIndexes[0].QuantizerType == null + && vector2Path.Equals(settings.IndexingPolicy.VectorIndexes[1].Path) + && VectorIndexType.QuantizedFlat.Equals(settings.IndexingPolicy.VectorIndexes[1].Type) + && QuantizerType.Product.Equals(settings.IndexingPolicy.VectorIndexes[1].QuantizerType) + && 3.Equals(settings.IndexingPolicy.VectorIndexes[1].QuantizationByteSize) + && settings.IndexingPolicy.VectorIndexes[1].VectorIndexShardKey.SequenceEqual(new string[] { "/Country" }) + && vector3Path.Equals(settings.IndexingPolicy.VectorIndexes[2].Path) + && VectorIndexType.DiskANN.Equals(settings.IndexingPolicy.VectorIndexes[2].Type) + && QuantizerType.Spherical.Equals(settings.IndexingPolicy.VectorIndexes[2].QuantizerType) + && 2.Equals(settings.IndexingPolicy.VectorIndexes[2].QuantizationByteSize) + && 35.Equals(settings.IndexingPolicy.VectorIndexes[2].IndexingSearchListSize) + && settings.IndexingPolicy.VectorIndexes[2].VectorIndexShardKey.SequenceEqual(new string[] { "/ZipCode" }) + ), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } + [TestMethod] + [DataRow("en-US")] + [DataRow("fr-FR")] + [DataRow("de-DE")] + [DataRow("it-IT")] + [DataRow("pt-BR")] + [DataRow("pt-PT")] + [DataRow("es-ES")] + public async Task ValidateFullTextPolicyWithAllSupportedLanguages(string language) + { + string fullTextPath1 = "/fts1", fullTextPath2 = "/fts2"; + + Collection fullTextPaths = new Collection() + { + new Cosmos.FullTextPath() + { + Path = fullTextPath1, + Language = language, + }, + new Cosmos.FullTextPath() + { + Path = fullTextPath2, + Language = language, + } + }; + + Mock mockContainerResponse = new Mock(); + mockContainerResponse + .Setup(x => x.StatusCode) + .Returns(HttpStatusCode.Created); + + Mock mockDatabase = new Mock(); + Mock mockClient = new Mock(); + mockDatabase.Setup(m => m.Client).Returns(mockClient.Object); + mockDatabase + .Setup(c => c.CreateContainerAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(mockContainerResponse.Object); + mockDatabase + .Setup(c => c.Id) + .Returns(Guid.NewGuid().ToString()); + + ContainerBuilder containerFluentDefinition = new ContainerBuilder( + mockDatabase.Object, + containerName, + partitionKey); + + ContainerResponse response = await containerFluentDefinition + .WithFullTextPolicy( + defaultLanguage: language, + fullTextPaths: fullTextPaths) + .Attach() + .WithIndexingPolicy() + .WithFullTextIndex() + .Path(fullTextPath1) + .Attach() + .WithFullTextIndex() + .Path(fullTextPath2) + .Attach() + .Attach() + .CreateAsync(); + + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); + + // Verify the correct language was passed + mockDatabase.Verify(c => c.CreateContainerAsync( + It.Is((settings) => + settings.FullTextPolicy.DefaultLanguage == language && + settings.FullTextPolicy.FullTextPaths.Count == 2 && + settings.FullTextPolicy.FullTextPaths.All(p => p.Language == language)), + It.IsAny(), + It.IsAny(), + It.IsAny()), Times.Once, + $"Failed to verify container creation with language: {language}"); + } + private static CosmosClientContext GetContext() { Mock cosmosClientContext = new Mock(); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GatewayAddressCacheTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GatewayAddressCacheTests.cs index aa088340fd..61787f537f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GatewayAddressCacheTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GatewayAddressCacheTests.cs @@ -60,7 +60,7 @@ public GatewayAddressCacheTests() } }; - this.partitionKeyRangeCache = new Mock(null, null, null, null, false); + this.partitionKeyRangeCache = new Mock(null, null, null, null, false, false); this.partitionKeyRangeCache .Setup(m => m.TryGetOverlappingRangesAsync( It.IsAny(), @@ -806,7 +806,7 @@ public async Task GlobalAddressResolver_OpenConnectionsToAllReplicasAsync_WhenIn .Returns(Task.FromResult(containerProperties)); string exceptionMessage = "Failed to lookup partition key ranges."; - Mock partitionKeyRangeCache = new(null, null, null, null, false); + Mock partitionKeyRangeCache = new(null, null, null, null, false, false); partitionKeyRangeCache .Setup(m => m.TryGetOverlappingRangesAsync( It.IsAny(), diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GatewayStoreModelTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GatewayStoreModelTest.cs index f5e9a95cea..28b6d0f7b9 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GatewayStoreModelTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GatewayStoreModelTest.cs @@ -9,8 +9,10 @@ namespace Microsoft.Azure.Cosmos using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; + using System.Linq; using System.Net; using System.Net.Http; + using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -147,7 +149,8 @@ public async Task TestRetries() eventSource, null, MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); @@ -209,7 +212,8 @@ public async Task PassesPropertiesFromDocumentServiceRequest() eventSource, null, MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); @@ -286,7 +290,7 @@ await GatewayStoreModel.ApplySessionTokenAsync( dsr, ConsistencyLevel.Session, new Mock().Object, - partitionKeyRangeCache: new Mock(null, null, null, null, false).Object, + partitionKeyRangeCache: new Mock(null, null, null, null, false, false).Object, clientCollectionCache: new Mock(new SessionContainer("testhost"), gatewayStoreModel, null, null, null, false).Object, globalEndpointManager: Mock.Of()); @@ -313,7 +317,7 @@ await GatewayStoreModel.ApplySessionTokenAsync( dsrQueryPlan, ConsistencyLevel.Session, new Mock().Object, - partitionKeyRangeCache: new Mock(null, null, null, null, false).Object, + partitionKeyRangeCache: new Mock(null, null, null, null, false, false).Object, clientCollectionCache: new Mock(new SessionContainer("testhost"), gatewayStoreModel, null, null, null, false).Object, globalEndpointManager: Mock.Of()); @@ -367,7 +371,7 @@ await GatewayStoreModel.ApplySessionTokenAsync( dsr, ConsistencyLevel.Session, new Mock().Object, - partitionKeyRangeCache: new Mock(null, null, null, null, false).Object, + partitionKeyRangeCache: new Mock(null, null, null, null, false, false).Object, clientCollectionCache: new Mock(new SessionContainer("testhost"), gatewayStoreModel, null, null, null, false).Object, globalEndpointManager: Mock.Of()); @@ -397,7 +401,7 @@ await GatewayStoreModel.ApplySessionTokenAsync( dsrNoSessionToken, ConsistencyLevel.Session, sessionContainer, - partitionKeyRangeCache: new Mock(null, null, null, null, false).Object, + partitionKeyRangeCache: new Mock(null, null, null, null, false, false).Object, clientCollectionCache: new Mock(new SessionContainer("testhost"), gatewayStoreModel, null, null, null, false).Object, globalEndpointManager: globalEndpointManager.Object); @@ -438,7 +442,7 @@ await GatewayStoreModel.ApplySessionTokenAsync( It.IsAny(), NoOpTrace.Singleton)).Returns(Task.FromResult(containerProperties)); - Mock mockPartitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null, null, false); + Mock mockPartitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null, null, false, false); mockPartitionKeyRangeCache.Setup(x => x.TryGetPartitionKeyRangeByIdAsync( containerProperties.ResourceId, partitionKeyRangeId, @@ -485,7 +489,7 @@ await GatewayStoreModel.ApplySessionTokenAsync( dsrSprocExecute, ConsistencyLevel.Session, new Mock().Object, - partitionKeyRangeCache: new Mock(null, null, null, null, false).Object, + partitionKeyRangeCache: new Mock(null, null, null, null, false, false).Object, clientCollectionCache: new Mock(new SessionContainer("testhost"), gatewayStoreModel, null, null, null, false).Object, globalEndpointManager: Mock.Of()); @@ -524,7 +528,7 @@ await GatewayStoreModel.ApplySessionTokenAsync( dsrNoSessionToken, ConsistencyLevel.Session, sessionContainer, - partitionKeyRangeCache: new Mock(null, null, null, null, false).Object, + partitionKeyRangeCache: new Mock(null, null, null, null, false, false).Object, clientCollectionCache: new Mock(new SessionContainer("testhost"), gatewayStoreModel, null, null, null, false).Object, globalEndpointManager: globalEndpointManager.Object); @@ -560,7 +564,8 @@ public async Task TestErrorResponsesProvideBody() eventSource, null, MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); @@ -622,7 +627,8 @@ private async Task GatewayStoreModel_Exception_UpdateSessionTokenOnKnownExceptio eventSource, null, MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); @@ -686,7 +692,8 @@ private async Task GatewayStoreModel_Exception_NotUpdateSessionTokenOnKnownExcep eventSource, null, MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); INameValueCollection headers = new RequestNameValueCollection(); headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, ConsistencyLevel.Session.ToString()); @@ -835,7 +842,8 @@ private async Task GatewayStoreModel_Exceptionless_UpdateSessionTokenOnKnownResp eventSource, null, MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); INameValueCollection headers = new RequestNameValueCollection(); @@ -889,7 +897,14 @@ public async Task GatewayStatsDurationTest() ResourceType.Document, HttpTimeoutPolicyDefault.InstanceShouldThrow503OnTimeout, clientSideRequestStatistics, - CancellationToken.None); + CancellationToken.None, + documentServiceRequest: new DocumentServiceRequest( + OperationType.Read, + ResourceType.Document, + $"dbs/dummy_db_id/colls/dummy_ct_id", + body: null, + AuthorizationTokenType.PrimaryMasterKey, + headers: null)); Assert.AreEqual(clientSideRequestStatistics.HttpResponseStatisticsList.Count, 2); // The duration is calculated using date times which can cause the duration to be slightly off. This allows for up to 15 Ms of variance. @@ -944,7 +959,8 @@ private async Task GatewayStoreModel_Exceptionless_NotUpdateSessionTokenOnKnownR eventSource, null, MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); INameValueCollection headers = new RequestNameValueCollection(); @@ -985,9 +1001,11 @@ public async Task GatewayStoreModel_AvoidGlobalSessionToken() eventSource, null, MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient()), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); + Mock clientCollectionCache = new Mock(new SessionContainer("testhost"), storeModel, null, null, null, false); - Mock partitionKeyRangeCache = new Mock(null, storeModel, clientCollectionCache.Object, endpointManager, false); + Mock partitionKeyRangeCache = new Mock(null, storeModel, clientCollectionCache.Object, endpointManager, false, false); sessionContainer.SetSessionToken( ResourceId.NewDocumentCollectionId(42, 129).DocumentCollectionId.ToString(), @@ -1080,11 +1098,12 @@ Task sendFunc(HttpRequestMessage request) eventSource, null, MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(messageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); Mock clientCollectionCache = new Mock(new SessionContainer("testhost"), storeModel, null, null, null, false); - Mock partitionKeyRangeCache = new Mock(null, storeModel, clientCollectionCache.Object, endpointManager, false); + Mock partitionKeyRangeCache = new Mock(null, storeModel, clientCollectionCache.Object, endpointManager, false, false); storeModel.SetCaches(partitionKeyRangeCache.Object, clientCollectionCache.Object); INameValueCollection headers = new RequestNameValueCollection(); @@ -1152,7 +1171,7 @@ await GatewayStoreModel.ApplySessionTokenAsync( documentServiceRequestToChild, ConsistencyLevel.Session, sessionContainer, - partitionKeyRangeCache: new Mock(null, null, null, null, false).Object, + partitionKeyRangeCache: new Mock(null, null, null, null, false, false).Object, clientCollectionCache: new Mock(sessionContainer, gatewayStoreModel, null, null, null, false).Object, globalEndpointManager: globalEndpointManager.Object); @@ -1218,7 +1237,7 @@ await GatewayStoreModel.ApplySessionTokenAsync( documentServiceRequestToChild, ConsistencyLevel.Session, sessionContainer, - partitionKeyRangeCache: new Mock(null, null, null, null, false).Object, + partitionKeyRangeCache: new Mock(null, null, null, null, false, false).Object, clientCollectionCache: new Mock(sessionContainer, gatewayStoreModel, null, null, null, false).Object, globalEndpointManager: globalEndpointManager.Object); @@ -1226,6 +1245,421 @@ await GatewayStoreModel.ApplySessionTokenAsync( }); } + [TestMethod] + [Owner("aavasthy")] + public async Task ThinClient_ProcessMessageAsync_Success_ShouldReturnDocumentServiceResponse() + { + string mockBase64 = "9AEAAMkAAAAIvhHfD23jSaynaR+gyTZ3AAAAAQIAByFUaHUsIDEzIEZlYiAyMDI1IDE0OjI1OjI4LjAyNCBHTVQEAAgmACIwMDAwYWQzZS0wMDAwLTAyMDAtMDAwMC02N2FlNjRjMDAwMDAiDgAIVABkb2N1bWVudFNpemU9NTEyMDA7ZG9jdW1lbnRzU2l6ZT01MjQyODgwMDtkb2N1bWVudHNDb3VudD0tMTtjb2xsZWN0aW9uU2l6ZT01MjQyODgwMDsPAAhBAGRvY3VtZW50U2l6ZT0wO2RvY3VtZW50c1NpemU9MTtkb2N1bWVudHNDb3VudD04O2NvbGxlY3Rpb25TaXplPTM7EAAHBDEuMTkTAAUKAAAAAAAAABUADgzDMAzDMBxAFwAIOgBkYnMvdGhpbi1jbGllbnQtdGVzdC1kYi9jb2xscy90aGluLWNsaWVudC10ZXN0LWNvbnRhaW5lci0xGAAIDABOSDF1QUo2QU5tMD0aAAUJAAAAAAAAAB4AAgMAAAAfAAIEAAAAIQAIAQAwJgACAQAAACkABQkAAAAAAAAAMAACAAAAADUAAgEAAAA6AAUKAAAAAAAAADsABQkAAAAAAAAAPgAIBQAtMSMxMFEADkjhehSuRxBAYwAIAQAweAAF//////////89AQAAeyJpZCI6IjNiMTFiNDM2LTViMTUtNGQwZS1iZWYwLWY1MzVmNjA0MTQxYyIsInBrIjoicGsiLCJuYW1lIjoiODM2MzI0NTA2IiwiZW1haWwiOiJhYmNAZGVmLmNvbSIsImJvZHkiOiJibGFibGEiLCJfcmlkIjoiTkgxdUFKNkFObTBKQUFBQUFBQUFBQT09IiwiX3NlbGYiOiJkYnMvTkgxdUFBPT0vY29sbHMvTkgxdUFKNkFObTA9L2RvY3MvTkgxdUFKNkFObTBKQUFBQUFBQUFBQT09LyIsIl9ldGFnIjoiXCIwMDAwYWQzZS0wMDAwLTAyMDAtMDAwMC02N2FlNjRjMDAwMDBcIiIsIl9hdHRhY2htZW50cyI6ImF0dGFjaG1lbnRzLyIsIl90cyI6MTczOTQ4MjMwNH0="; + + HttpResponseMessage successResponse = new HttpResponseMessage(HttpStatusCode.Created) + { + Content = new ByteArrayContent(Convert.FromBase64String(mockBase64)) + }; + + MockThinClientStoreClient thinClientStoreClient = new MockThinClientStoreClient( + (request, resourceType, uri, endpoint, globalDatabaseAccountName, clientCollectionCache, cancellationToken) => + { + Stream responseBody = successResponse.Content.ReadAsStream(); + INameValueCollection headers = new StoreResponseNameValueCollection(); + return Task.FromResult(new DocumentServiceResponse(responseBody, headers, successResponse.StatusCode)); + }); + + Mock mockDocumentClient = new Mock(); + mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); + mockDocumentClient + .Setup(c => c.GetDatabaseAccountInternalAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new AccountProperties()); + + UserAgentContainer userAgentContainer = new UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); + GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, new ConnectionPolicy()); + SessionContainer sessionContainer = new SessionContainer("testhost"); + GatewayStoreModel storeModel = new GatewayStoreModel( + endpointManager, + sessionContainer, + ConsistencyLevel.Session, + new DocumentClientEventSource(), + null, + null, + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: true, + userAgentContainer); + + ClientCollectionCache clientCollectionCache = new Mock( + sessionContainer, + storeModel, + null, + null, + null, + false).Object; + + PartitionKeyRangeCache partitionKeyRangeCache = new Mock( + null, + storeModel, + clientCollectionCache, + endpointManager, + false, false).Object; + + storeModel.SetCaches(partitionKeyRangeCache, clientCollectionCache); + + ReplaceThinClientStoreClientField(storeModel, thinClientStoreClient); + + DocumentServiceRequest request = DocumentServiceRequest.Create( + operationType: OperationType.Create, + resourceType: ResourceType.Document, + resourceId: "NH1uAJ6ANm0=", + body: null, + authorizationTokenType: AuthorizationTokenType.PrimaryMasterKey); + + DocumentServiceResponse response = await storeModel.ProcessMessageAsync(request); + + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); + } + + [TestMethod] + [Owner("dkunda")] + public async Task ThinClient_ProcessMessageAsync_WithUnsupportedOperations_ShouldFallbackToGatewayModeAndReturnDocumentServiceResponse() + { + // Arrange + HttpResponseMessage successResponse = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Response") }; + Mock mockCosmosHttpClient = new Mock(); + mockCosmosHttpClient.Setup(client => client.SendHttpAsync( + It.IsAny>>(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(successResponse); + + DocumentServiceRequest request = DocumentServiceRequest.Create( + operationType: OperationType.QueryPlan, + resourceType: ResourceType.Document, + resourceId: "NH1uAJ6ANm0=", + body: null, + authorizationTokenType: AuthorizationTokenType.PrimaryMasterKey); + + Mock docClientMulti = new Mock(); + docClientMulti.Setup(c => c.ServiceEndpoint).Returns(new Uri("http://localhost")); + docClientMulti + .Setup(c => c.GetDatabaseAccountInternalAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new AccountProperties()); + + ConnectionPolicy policy = new ConnectionPolicy + { + UseMultipleWriteLocations = true + }; + + GlobalEndpointManager multiEndpointMgr = new GlobalEndpointManager(docClientMulti.Object, policy); + SessionContainer sessionContainer = new SessionContainer("testhost"); + UserAgentContainer userAgentContainer = new UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); + + GatewayStoreModel storeModel = new GatewayStoreModel( + endpointManager: multiEndpointMgr, + sessionContainer: sessionContainer, + defaultConsistencyLevel: ConsistencyLevel.Session, + eventSource: new DocumentClientEventSource(), + serializerSettings: null, + httpClient: mockCosmosHttpClient.Object, + globalPartitionEndpointManager: GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: true, + userAgentContainer: userAgentContainer); + + ClientCollectionCache clientCollectionCache = new Mock( + sessionContainer, + storeModel, + null, + null, + null, + false).Object; + + PartitionKeyRangeCache partitionKeyRangeCache = new Mock( + null, + storeModel, + clientCollectionCache, + multiEndpointMgr, + false, false).Object; + + storeModel.SetCaches(partitionKeyRangeCache, clientCollectionCache); + + // Act + DocumentServiceResponse response = await storeModel.ProcessMessageAsync(request); + + // Assert + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + } + + [TestMethod] + [Owner("aavasthy")] + public void ThinClient_Dispose_ShouldDisposeThinClientStoreClient() + { + bool disposeCalled = false; + + MockThinClientStoreClient thinClientStoreClient = new MockThinClientStoreClient( + (request, resourceType, uri, endpoint, globalDatabaseAccountName, clientCollectionCache, cancellationToken) => + throw new NotImplementedException(), + () => disposeCalled = true); + + UserAgentContainer userAgentContainer = new UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); + GlobalEndpointManager endpointManager = new GlobalEndpointManager(Mock.Of(), new ConnectionPolicy()); + SessionContainer sessionContainer = new SessionContainer("testhost"); + GatewayStoreModel storeModel = new GatewayStoreModel( + endpointManager, + sessionContainer, + ConsistencyLevel.Session, + new DocumentClientEventSource(), + null, + null, + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: true, + userAgentContainer); + + ReplaceThinClientStoreClientField(storeModel, thinClientStoreClient); + + storeModel.Dispose(); + Assert.IsTrue(disposeCalled, "Expected Dispose to be called on ThinClientStoreClient."); + } + + [TestMethod] + [Owner("aavasthy")] + public async Task ThinClient_ProcessMessageAsync_404_ShouldThrowDocumentClientException() + { + // Arrange + MockThinClientStoreClient thinClientStoreClient = new MockThinClientStoreClient( + (request, resourceType, uri, endpoint, globalDatabaseAccountName, clientCollectionCache, cancellationToken) => + throw new DocumentClientException( + message: "Not Found", + innerException: null, + responseHeaders: new StoreResponseNameValueCollection(), + statusCode: HttpStatusCode.NotFound, + requestUri: uri)); + + Mock mockDocumentClient = new Mock(); + mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); + mockDocumentClient + .Setup(c => c.GetDatabaseAccountInternalAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new AccountProperties()); + + UserAgentContainer userAgentContainer = new UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); + GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, new ConnectionPolicy()); + SessionContainer sessionContainer = new SessionContainer("testhost"); + + GatewayStoreModel storeModel = new GatewayStoreModel( + endpointManager, + sessionContainer, + ConsistencyLevel.Session, + new DocumentClientEventSource(), + null, + null, + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: true, + userAgentContainer); + + ClientCollectionCache clientCollectionCache = new Mock( + sessionContainer, + storeModel, + null, + null, + null, + false).Object; + + PartitionKeyRangeCache partitionKeyRangeCache = new Mock( + null, + storeModel, + clientCollectionCache, + endpointManager, + false, false).Object; + + storeModel.SetCaches(partitionKeyRangeCache, clientCollectionCache); + + ReplaceThinClientStoreClientField(storeModel, thinClientStoreClient); + + DocumentServiceRequest request = DocumentServiceRequest.Create( + operationType: OperationType.Read, + resourceType: ResourceType.Document, + resourceId: "NH1uAJ6ANm0=", + body: null, + authorizationTokenType: AuthorizationTokenType.PrimaryMasterKey); + + // Act & Assert + await Assert.ThrowsExceptionAsync( + async () => await storeModel.ProcessMessageAsync(request), + "Expected 404 DocumentClientException from the final thinClientStore call"); + } + + [TestMethod] + [Owner("aavasthy")] + public async Task ThinClient_PartitionLevelFailoverEnabled_ResolvesPartitionKeyRangeAndCallsLocationOverride() + { + Mock mockDocumentClient = new Mock(); + mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); + mockDocumentClient + .Setup(c => c.GetDatabaseAccountInternalAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new AccountProperties()); + + ConnectionPolicy connectionPolicy = new ConnectionPolicy(); + GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, connectionPolicy); + + Mock globalPartitionEndpointManager = new Mock(); + + globalPartitionEndpointManager + .Setup(m => m.IsPartitionLevelAutomaticFailoverEnabled()) + .Returns(true) + .Verifiable(); + + globalPartitionEndpointManager + .Setup(m => m.TryAddPartitionLevelLocationOverride(It.IsAny())) + .Returns(true) + .Verifiable(); + + ISessionContainer sessionContainer = new Mock().Object; + DocumentClientEventSource eventSource = new Mock().Object; + JsonSerializerSettings serializerSettings = new JsonSerializerSettings(); + CosmosHttpClient httpClient = new Mock().Object; + UserAgentContainer userAgentContainer = new UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); + + GatewayStoreModel storeModel = new GatewayStoreModel( + endpointManager, + sessionContainer, + ConsistencyLevel.Session, + eventSource, + serializerSettings, + httpClient, + globalPartitionEndpointManager.Object, + isThinClientEnabled: true, + userAgentContainer); + + Mock mockCollectionCache = new Mock( + sessionContainer, + storeModel, + null, + null, + null, + false); + + ContainerProperties containerProperties = new ContainerProperties("test", "/pk"); + typeof(ContainerProperties) + .GetProperty("ResourceId", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) + ?.SetValue(containerProperties, "testCollectionRid"); + containerProperties.PartitionKeyPath = "/pk"; + + mockCollectionCache + .Setup(c => c.ResolveCollectionAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(containerProperties); + + Mock mockPartitionKeyRangeCache = new Mock( + null, + storeModel, + mockCollectionCache.Object, + endpointManager, + false, false); + + PartitionKeyRange pkRange = new PartitionKeyRange { Id = "0", MinInclusive = "", MaxExclusive = "FF" }; + List pkRanges = new List { pkRange }; + IEnumerable> rangeTuples = pkRanges.Select(r => Tuple.Create(r, (ServiceIdentity)null)); + CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap(rangeTuples, "testCollectionRid", false); + + mockPartitionKeyRangeCache + .Setup(c => c.TryLookupAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(routingMap); + + storeModel.SetCaches(mockPartitionKeyRangeCache.Object, mockCollectionCache.Object); + + DocumentServiceRequest request = CreatePartitionedDocumentRequest(); + + MockThinClientStoreClient mockThinClientStoreClient = new MockThinClientStoreClient( + (DocumentServiceRequest req, ResourceType resourceType, Uri uri, Uri endpoint, string globalDatabaseAccountName, ClientCollectionCache clientCollectionCache, CancellationToken cancellationToken) => + { + MemoryStream stream = new MemoryStream(new byte[] { 1, 2, 3 }); + INameValueCollection headers = new StoreResponseNameValueCollection(); + return Task.FromResult(new DocumentServiceResponse(stream, headers, HttpStatusCode.OK)); + }); + + ReplaceThinClientStoreClientField(storeModel, mockThinClientStoreClient); + + // Act + await storeModel.ProcessMessageAsync(request); + + // Assert + globalPartitionEndpointManager.Verify(m => m.TryAddPartitionLevelLocationOverride(It.IsAny()), Times.Once()); + } + + [TestMethod] + [Owner("aavasthy")] + public void ThinClient_CircuitBreaker_MarksPartitionUnavailableOnRepeatedFailures() + { + Mock mockDocumentClient = new Mock(); + mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); + ConnectionPolicy connectionPolicy = new ConnectionPolicy(); + GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, connectionPolicy); + Mock globalPartitionEndpointManager = new Mock(); + globalPartitionEndpointManager + .Setup(m => m.TryAddPartitionLevelLocationOverride(It.IsAny())) + .Returns(true); + + globalPartitionEndpointManager + .Setup(m => m.TryMarkEndpointUnavailableForPartitionKeyRange(It.IsAny())) + .Returns(true) + .Verifiable(); + + globalPartitionEndpointManager + .Setup(m => m.IncrementRequestFailureCounterAndCheckIfPartitionCanFailover(It.IsAny())) + .Returns(true); + + ISessionContainer sessionContainer = new Mock().Object; + DocumentClientEventSource eventSource = new Mock().Object; + JsonSerializerSettings serializerSettings = new JsonSerializerSettings(); + CosmosHttpClient httpClient = new Mock().Object; + UserAgentContainer userAgentContainer = new UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); + + GatewayStoreModel storeModel = new GatewayStoreModel( + endpointManager, + sessionContainer, + ConsistencyLevel.Session, + eventSource, + serializerSettings, + httpClient, + globalPartitionEndpointManager.Object, + isThinClientEnabled: true, + userAgentContainer); + + TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); + + DocumentServiceRequest request = CreatePartitionedDocumentRequest(); + + for (int i = 0; i < 3; i++) + { + globalPartitionEndpointManager.Object.IncrementRequestFailureCounterAndCheckIfPartitionCanFailover(request); + } + + globalPartitionEndpointManager.Object.TryMarkEndpointUnavailableForPartitionKeyRange(request); + + globalPartitionEndpointManager.Verify(m => m.TryMarkEndpointUnavailableForPartitionKeyRange(It.IsAny()), Times.Once()); + } + + private static void ReplaceThinClientStoreClientField(GatewayStoreModel model, ThinClientStoreClient newClient) + { + FieldInfo field = typeof(GatewayStoreModel).GetField( + "thinClientStoreClient", + BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new InvalidOperationException("Could not find 'thinClientStoreClient' field on GatewayStoreModel"); + + field.SetValue(model, newClient); + } + + private static DocumentServiceRequest CreatePartitionedDocumentRequest() + { + DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Document, + "/dbs/test/colls/test/docs/test", + AuthorizationTokenType.PrimaryMasterKey); + request.Headers[HttpConstants.HttpHeaders.PartitionKey] = "[\"test\"]"; + request.RequestContext = new DocumentServiceRequestContext(); + return request; + } + private class MockMessageHandler : HttpMessageHandler { private readonly Func> sendFunc; @@ -1287,10 +1721,11 @@ static async Task messageHandler(HttpRequestMessage request eventSource, null, MockCosmosUtil.CreateCosmosHttpClient(() => new HttpClient(httpMessageHandler)), - GlobalPartitionEndpointManagerNoOp.Instance); + GlobalPartitionEndpointManagerNoOp.Instance, + isThinClientEnabled: false); ClientCollectionCache clientCollectionCache = new Mock(new SessionContainer("testhost"), storeModel, null, null, null, false).Object; - PartitionKeyRangeCache partitionKeyRangeCache = new Mock(null, storeModel, clientCollectionCache, endpointManager, false).Object; + PartitionKeyRangeCache partitionKeyRangeCache = new Mock(null, storeModel, clientCollectionCache, endpointManager, false, false).Object; storeModel.SetCaches(partitionKeyRangeCache, clientCollectionCache); await executeWithGatewayStoreModel(storeModel); @@ -1308,5 +1743,50 @@ private async Task TestGatewayStoreModelProcessMessageAsync(GatewayStoreModel st await storeModel.ProcessMessageAsync(request); } } + + internal class MockThinClientStoreClient : ThinClientStoreClient + { + private readonly Func> invokeAsyncFunc; + private readonly Action onDispose; + + public MockThinClientStoreClient( + Func> invokeAsyncFunc, + Action onDispose = null) + : base( + httpClient: null, + eventSource: null, + userAgentContainer: new UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"), + globalPartitionEndpointManager: GlobalPartitionEndpointManagerNoOp.Instance, + serializerSettings: null) + { + this.invokeAsyncFunc = invokeAsyncFunc; + this.onDispose = onDispose; + } + + public override async Task InvokeAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + return await this.invokeAsyncFunc( + request, + resourceType, + physicalAddress, + thinClientEndpoint, + globalDatabaseAccountName, + clientCollectionCache, + cancellationToken); + } + + public override void Dispose() + { + base.Dispose(); + this.onDispose?.Invoke(); + } + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GlobalEndpointManagerTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GlobalEndpointManagerTest.cs index 9b1aa640c5..8e2947234e 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GlobalEndpointManagerTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/GlobalEndpointManagerTest.cs @@ -24,6 +24,95 @@ namespace Microsoft.Azure.Cosmos [TestClass] public class GlobalEndpointManagerTest { + /// + /// Tests for + /// + [TestMethod] + [TestCategory("Flaky")] + public async Task EndpointFailureMockTest() + { + Environment.SetEnvironmentVariable("MinimumIntervalForNonForceRefreshLocationInMS", "100"); + Environment.SetEnvironmentVariable("UnavailableLocationsExpirationTimeInSeconds", "2"); + try + { + // Setup dummpy read locations for the database account + Collection readableLocations = new Collection(); + + AccountRegion writeLocation = new AccountRegion + { + Name = "WriteLocation", + Endpoint = "https://writeendpoint.net/" + }; + + AccountRegion readLocation1 = new AccountRegion + { + Name = "ReadLocation1", + Endpoint = "https://readendpoint1.net/" + }; + + AccountRegion readLocation2 = new AccountRegion + { + Name = "ReadLocation2", + Endpoint = "https://readendpoint2.net/" + }; + + readableLocations.Add(writeLocation); + readableLocations.Add(readLocation1); + readableLocations.Add(readLocation2); + + AccountProperties databaseAccount = new AccountProperties + { + ReadLocationsInternal = readableLocations + }; + + //Setup mock owner "document client" + Mock mockOwner = new Mock(); + mockOwner.Setup(owner => owner.ServiceEndpoint).Returns(new Uri("https://defaultendpoint.net/")); + + int getAccountInfoCount = 0; + mockOwner.Setup(owner => owner.GetDatabaseAccountInternalAsync(It.IsAny(), It.IsAny())) + .Callback(() => getAccountInfoCount++) + .ReturnsAsync(databaseAccount); + + //Create connection policy and populate preferred locations + ConnectionPolicy connectionPolicy = new ConnectionPolicy(); + connectionPolicy.PreferredLocations.Add("ReadLocation1"); + connectionPolicy.PreferredLocations.Add("ReadLocation2"); + + using (GlobalEndpointManager globalEndpointManager = new GlobalEndpointManager(mockOwner.Object, connectionPolicy)) + { + globalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(databaseAccount); + Assert.AreEqual(globalEndpointManager.ReadEndpoints[0], new Uri(readLocation1.Endpoint)); + + //Mark each of the read locations as unavailable and validate that the read endpoint switches to the next preferred region / default endpoint. + globalEndpointManager.MarkEndpointUnavailableForRead(globalEndpointManager.ReadEndpoints[0]); + await globalEndpointManager.RefreshLocationAsync(); + Assert.AreEqual(new Uri(readLocation2.Endpoint), globalEndpointManager.ReadEndpoints[0], "Read endpoint did not switch to location 2 after marking location 1 as unavailable."); + + globalEndpointManager.MarkEndpointUnavailableForRead(globalEndpointManager.ReadEndpoints[0]); + await globalEndpointManager.RefreshLocationAsync(); + Assert.AreEqual(globalEndpointManager.WriteEndpoints[0], globalEndpointManager.ReadEndpoints[0]); + + getAccountInfoCount = 0; + //Sleep 3 seconds for the unavailable endpoint entry to expire and background refresh timer to kick in + await Task.Delay(TimeSpan.FromSeconds(3)); + Assert.IsTrue(getAccountInfoCount > 0, "Callback is not working. There should be at least one call in this time frame."); + + await globalEndpointManager.RefreshLocationAsync(); + Assert.AreEqual(new Uri(readLocation1.Endpoint), globalEndpointManager.ReadEndpoints[0], "Read endpoint did not switch back to location 1 after the unavailable entry expired."); + } + + Assert.IsTrue(getAccountInfoCount > 0, "Callback is not working. There should be at least one call in this time frame."); + getAccountInfoCount = 0; + await Task.Delay(TimeSpan.FromSeconds(5)); + Assert.IsTrue(getAccountInfoCount <= 1, "There should be at most 1 call to refresh tied to the background refresh happening while Dispose cancels the internal CancellationToken"); + } + finally + { + Environment.SetEnvironmentVariable("MinimumIntervalForNonForceRefreshLocationInMS", null); + Environment.SetEnvironmentVariable("UnavailableLocationsExpirationTimeInSeconds", null); + } + } [TestMethod] public async Task ValidateCancellationTokenLogicForGetDatabaseAccountFromAnyLocationAsync() @@ -517,7 +606,7 @@ public async Task ReadLocationRemoveAndAddMockTest() string originalConfigValue = Environment.GetEnvironmentVariable("MinimumIntervalForNonForceRefreshLocationInMS"); Environment.SetEnvironmentVariable("MinimumIntervalForNonForceRefreshLocationInMS", "1000"); - // Setup dummpy read locations for the database account + // Setup dummy read locations for the database account Collection readableLocations = new Collection(); AccountRegion writeLocation = new AccountRegion @@ -560,14 +649,14 @@ public async Task ReadLocationRemoveAndAddMockTest() using GlobalEndpointManager globalEndpointManager = new GlobalEndpointManager(mockOwner.Object, connectionPolicy); globalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(databaseAccount); - Assert.AreEqual(globalEndpointManager.ReadEndpoints[0], new Uri(readLocation1.Endpoint)); + Assert.AreEqual(new Uri(readLocation1.Endpoint), globalEndpointManager.ReadEndpoints[0], "Read endpoint is not location 1 as expected."); //Remove location 1 from read locations and validate that the read endpoint switches to the next preferred location readableLocations.Remove(readLocation1); databaseAccount.ReadLocationsInternal = readableLocations; globalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(databaseAccount); - Assert.AreEqual(globalEndpointManager.ReadEndpoints[0], new Uri(readLocation2.Endpoint)); + Assert.AreEqual(new Uri(readLocation2.Endpoint), globalEndpointManager.ReadEndpoints[0], "Read endpoint did not switch to location 2 after removing location 1."); //Add location 1 back to read locations and validate that location 1 becomes the read endpoint again. readableLocations.Add(readLocation1); @@ -593,7 +682,13 @@ void TraceHandler(string message) await Task.Delay(500); } - Assert.AreEqual(globalEndpointManager.ReadEndpoints[0], new Uri(readLocation1.Endpoint)); + ValueStopwatch endpointUpdateStopwatch = ValueStopwatch.StartNew(); + while (globalEndpointManager.ReadEndpoints[0] != new Uri(readLocation1.Endpoint)) + { + Assert.IsTrue(endpointUpdateStopwatch.Elapsed.TotalSeconds < 1, + $"Read endpoint did not switch back to location 1 after adding it back. Current endpoint: {globalEndpointManager.ReadEndpoints[0]}"); + await Task.Delay(100); + } Environment.SetEnvironmentVariable("MinimumIntervalForNonForceRefreshLocationInMS", originalConfigValue); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/CosmosSystemTextJsonSerializerTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/CosmosSystemTextJsonSerializerTest.cs index 42f5b9ac65..268caef4fb 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/CosmosSystemTextJsonSerializerTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/CosmosSystemTextJsonSerializerTest.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Reflection; + using System.Text.Json; using Microsoft.Azure.Cosmos.Tests.Poco.STJ; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -176,5 +177,54 @@ public void TestSerializeMemberName() Assert.AreEqual(member.Name, this.stjSerializer.SerializeMemberName(member)); } } + + [TestMethod] + public void TestPolymorphicSerialization_IncludesTypeDiscriminator() + { + // Arrange. + Shape circle = new Circle + { + Id = "circle", + Color = "Red", + Radius = 5.0 + }; + + // Act. + Stream serializedStream = this.stjSerializer.ToStream(circle); + using StreamReader reader = new(serializedStream); + string json = reader.ReadToEnd(); + + // Assert. + using JsonDocument jsonDocument = JsonDocument.Parse(json); + JsonElement rootElement = jsonDocument.RootElement; + + Assert.AreEqual("Circle", rootElement.GetProperty("$type").GetString()); + Assert.AreEqual(5.0, rootElement.GetProperty("radius").GetDouble()); + } + + [TestMethod] + public void TestPolymorphicSerialization_SerializeDeserialize_PreservesType() + { + // Arrange. + Shape original = new Circle + { + Id = "circle", + Color = "Green", + Radius = 7.5 + }; + + // Act. + Stream serializedStream = this.stjSerializer.ToStream(original); + Shape deserialized = this.stjSerializer.FromStream(serializedStream); + + // Assert. + Assert.IsNotNull(deserialized); + Assert.IsInstanceOfType(deserialized, typeof(Circle)); + + Circle deserializedCircle = (Circle)deserialized; + Assert.AreEqual(original.Id, deserializedCircle.Id); + Assert.AreEqual(original.Color, deserializedCircle.Color); + Assert.AreEqual(((Circle)original).Radius, deserializedCircle.Radius); + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonNavigatorTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonNavigatorTests.cs index 137670de99..91aa5ed9af 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonNavigatorTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonNavigatorTests.cs @@ -377,7 +377,7 @@ public void CuratedDocumentCombinedScriptsDataTest() [Ignore] // This test takes too long public void CuratedDocumentCountriesTest() { - JsonNavigatorTests.VerifyNavigatorWithCuratedDoc("countries", performExtraChecks: false); + JsonNavigatorTests.VerifyNavigatorWithCuratedDoc("countries"); } [TestMethod] @@ -398,7 +398,7 @@ public void CuratedDocumentLastFMTest() [Owner("mayapainter")] public void CuratedDocumentLogDataTest() { - JsonNavigatorTests.VerifyNavigatorWithCuratedDoc("LogData.json", performExtraChecks: false); + JsonNavigatorTests.VerifyNavigatorWithCuratedDoc("LogData.json"); } [TestMethod] @@ -478,20 +478,18 @@ public void CuratedDocumentXpertEventsTest() JsonNavigatorTests.VerifyNavigatorWithCuratedDoc("XpertEvents"); } - private static void VerifyNavigatorWithCuratedDoc(string filename, bool performExtraChecks = true) + private static void VerifyNavigatorWithCuratedDoc(string filename) { string json = JsonTestUtils.LoadJsonCuratedDocument(filename); #if true json = JsonTestUtils.RandomSampleJson(json, maxNumberOfItems: 10); #endif - JsonNavigatorTests.VerifyNavigator(json, performExtraChecks); + JsonNavigatorTests.VerifyNavigator(json); } #endregion - private static void VerifyNavigator( - string input, - bool performExtraChecks = true) + private static void VerifyNavigator(string input) { CultureInfo defaultCultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture; @@ -534,9 +532,8 @@ private static void VerifyNavigator( // Test foreach (IJsonNavigator jsonNavigator in new IJsonNavigator[] { textNavigator, binaryNavigator, binaryNavigatorWithUserStringEncoding }) { - IJsonNavigatorNode rootNode = jsonNavigator.GetRootNode(); - JsonToken[] tokensFromNavigator = JsonNavigatorTests.GetTokensFromNode(rootNode, jsonNavigator, performExtraChecks); - Assert.AreEqual(tokensFromNavigator.Length, tokensFromReader.Length); + IReadOnlyList tokensFromNavigator = GetTokensFromNavigator(jsonNavigator); + Assert.AreEqual(tokensFromNavigator.Count, tokensFromReader.Length); IEnumerable<(JsonToken, JsonToken)> zippedTokens = tokensFromNavigator.Zip(tokensFromReader, (first, second) => (first, second)); foreach ((JsonToken tokenFromNavigator, JsonToken tokenFromReader) in zippedTokens) { @@ -547,6 +544,7 @@ private static void VerifyNavigator( } // Test materialize + IJsonNavigatorNode rootNode = jsonNavigator.GetRootNode(); JToken materializedToken = CosmosElement.Dispatch(jsonNavigator, rootNode).Materialize(); try @@ -577,110 +575,100 @@ private static void VerifyNavigator( } } - private static JsonToken[] GetTokensFromNode(IJsonNavigatorNode node, IJsonNavigator navigator, bool performCorrectnessCheck) + private static IReadOnlyList GetTokensFromNavigator(IJsonNavigator navigator) { - JsonNodeType nodeType = navigator.GetNodeType(node); - return nodeType switch - { - JsonNodeType.Null => new JsonToken[] { JsonToken.Null() }, - JsonNodeType.False => new JsonToken[] { JsonToken.Boolean(false) }, - JsonNodeType.True => new JsonToken[] { JsonToken.Boolean(true) }, - JsonNodeType.Number => new JsonToken[] { JsonToken.Number(navigator.GetNumberValue(node)) }, - JsonNodeType.String => new JsonToken[] { JsonToken.String(navigator.GetStringValue(node)) }, - JsonNodeType.Array => JsonNavigatorTests.GetTokensFromArrayNode(node, navigator, performCorrectnessCheck), - JsonNodeType.Object => JsonNavigatorTests.GetTokensFromObjectNode(node, navigator, performCorrectnessCheck), - JsonNodeType.FieldName => new JsonToken[] { JsonToken.FieldName(navigator.GetStringValue(node)) }, - JsonNodeType.Int8 => new JsonToken[] { JsonToken.Int8(navigator.GetInt8Value(node)) }, - JsonNodeType.Int16 => new JsonToken[] { JsonToken.Int16(navigator.GetInt16Value(node)) }, - JsonNodeType.Int32 => new JsonToken[] { JsonToken.Int32(navigator.GetInt32Value(node)) }, - JsonNodeType.Int64 => new JsonToken[] { JsonToken.Int64(navigator.GetInt64Value(node)) }, - JsonNodeType.UInt32 => new JsonToken[] { JsonToken.UInt32(navigator.GetUInt32Value(node)) }, - JsonNodeType.Float32 => new JsonToken[] { JsonToken.Float32(navigator.GetFloat32Value(node)) }, - JsonNodeType.Float64 => new JsonToken[] { JsonToken.Float64(navigator.GetFloat64Value(node)) }, - JsonNodeType.Guid => new JsonToken[] { JsonToken.Guid(navigator.GetGuidValue(node)) }, - JsonNodeType.Binary => new JsonToken[] { JsonToken.Binary(navigator.GetBinaryValue(node)) }, - _ => throw new InvalidOperationException(), - }; + IJsonNavigatorNode rootNode = navigator.GetRootNode(); + List tokens = new List(); + JsonNavigatorTests.AppendTokensFromNode(rootNode, navigator, tokens); + return tokens; } - private static JsonToken[] GetTokensFromObjectNode(IJsonNavigatorNode node, IJsonNavigator navigator, bool performCorrectnessCheck) + private static void AppendTokensFromNode(IJsonNavigatorNode node, IJsonNavigator navigator, List tokens) { - // Get the tokens through .GetObjectProperties - List tokensFromGetProperties = new List(); - IEnumerable properties = navigator.GetObjectProperties(node); - - tokensFromGetProperties.Add(JsonToken.ObjectStart()); - foreach (ObjectProperty property in properties) - { - string fieldname = navigator.GetStringValue(property.NameNode); - tokensFromGetProperties.Add(JsonToken.FieldName(fieldname)); - tokensFromGetProperties.AddRange(JsonNavigatorTests.GetTokensFromNode(property.ValueNode, navigator, performCorrectnessCheck)); - } - tokensFromGetProperties.Add(JsonToken.ObjectEnd()); - - if (performCorrectnessCheck) + JsonNodeType nodeType = navigator.GetNodeType(node); + switch (nodeType) { - // Get the tokens again through .TryGetObjectProperty - List tokensFromTryGetProperty = new List - { - JsonToken.ObjectStart() - }; - foreach (ObjectProperty objectProperty in properties) - { - string fieldname = navigator.GetStringValue(objectProperty.NameNode); - if (navigator.TryGetObjectProperty(node, fieldname, out ObjectProperty propertyFromTryGetProperty)) - { - tokensFromTryGetProperty.Add(JsonToken.FieldName(fieldname)); - tokensFromTryGetProperty.AddRange(JsonNavigatorTests.GetTokensFromNode(propertyFromTryGetProperty.ValueNode, navigator, performCorrectnessCheck)); - } - else - { - Assert.Fail($"Failed to get object property with name: {fieldname}"); - } - } - tokensFromTryGetProperty.Add(JsonToken.ObjectEnd()); - Assert.AreEqual(properties.Count(), navigator.GetObjectPropertyCount(node)); - Assert.IsTrue(tokensFromGetProperties.SequenceEqual(tokensFromTryGetProperty)); - } - - return tokensFromGetProperties.ToArray(); + case JsonNodeType.Array: + AppendTokensFromArrayNode(node, navigator, tokens); + break; + case JsonNodeType.Binary: + tokens.Add(JsonToken.Binary(navigator.GetBinaryValue(node))); + break; + case JsonNodeType.FieldName: + tokens.Add(JsonToken.FieldName(navigator.GetStringValue(node))); + break; + case JsonNodeType.Float32: + tokens.Add(JsonToken.Float32(navigator.GetFloat32Value(node))); + break; + case JsonNodeType.Float64: + tokens.Add(JsonToken.Float64(navigator.GetFloat64Value(node))); + break; + case JsonNodeType.Guid: + tokens.Add(JsonToken.Guid(navigator.GetGuidValue(node))); + break; + case JsonNodeType.Int16: + tokens.Add(JsonToken.Int16(navigator.GetInt16Value(node))); + break; + case JsonNodeType.Int32: + tokens.Add(JsonToken.Int32(navigator.GetInt32Value(node))); + break; + case JsonNodeType.Int64: + tokens.Add(JsonToken.Int64(navigator.GetInt64Value(node))); + break; + case JsonNodeType.Int8: + tokens.Add(JsonToken.Int8(navigator.GetInt8Value(node))); + break; + case JsonNodeType.Null: + tokens.Add(JsonToken.Null()); + break; + case JsonNodeType.False: + tokens.Add(JsonToken.Boolean(false)); + break; + case JsonNodeType.True: + tokens.Add(JsonToken.Boolean(true)); + break; + case JsonNodeType.Number: + tokens.Add(JsonToken.Number(navigator.GetNumberValue(node))); + break; + case JsonNodeType.Object: + AppendTokensFromObjectNode(node, navigator, tokens); + break; + case JsonNodeType.String: + tokens.Add(JsonToken.String(navigator.GetStringValue(node))); + break; + case JsonNodeType.UInt32: + tokens.Add(JsonToken.UInt32(navigator.GetUInt32Value(node))); + break; + default: + throw new InvalidOperationException(); + }; } - private static JsonToken[] GetTokensFromArrayNode(IJsonNavigatorNode node, IJsonNavigator navigator, bool performCorrectnessCheck) + private static void AppendTokensFromArrayNode(IJsonNavigatorNode node, IJsonNavigator navigator, List tokens) { - // Get tokens once through IEnumerable - List tokensFromIEnumerable = new List(); IEnumerable arrayItems = navigator.GetArrayItems(node); - tokensFromIEnumerable.Add(JsonToken.ArrayStart()); + tokens.Add(JsonToken.ArrayStart()); foreach (IJsonNavigatorNode arrayItem in arrayItems) { - tokensFromIEnumerable.AddRange(JsonNavigatorTests.GetTokensFromNode(arrayItem, navigator, performCorrectnessCheck)); + AppendTokensFromNode(arrayItem, navigator, tokens); } + tokens.Add(JsonToken.ArrayEnd()); + } - tokensFromIEnumerable.Add(JsonToken.ArrayEnd()); + private static void AppendTokensFromObjectNode(IJsonNavigatorNode node, IJsonNavigator navigator, List tokens) + { + IEnumerable properties = navigator.GetObjectProperties(node); - if (performCorrectnessCheck) + tokens.Add(JsonToken.ObjectStart()); + foreach (ObjectProperty property in properties) { - // Get tokens once again through indexer - List tokensFromIndexer = new List - { - JsonToken.ArrayStart() - }; - - int itemCount = navigator.GetArrayItemCount(node); - for (int i = 0; i < itemCount; ++i) - { - tokensFromIndexer.AddRange(JsonNavigatorTests.GetTokensFromNode(navigator.GetArrayItemAt(node, i), navigator, performCorrectnessCheck)); - } - - tokensFromIndexer.Add(JsonToken.ArrayEnd()); + string fieldname = navigator.GetStringValue(property.NameNode); + tokens.Add(JsonToken.FieldName(fieldname)); - Assert.AreEqual(arrayItems.Count(), navigator.GetArrayItemCount(node)); - Assert.IsTrue(tokensFromIEnumerable.SequenceEqual(tokensFromIndexer)); + AppendTokensFromNode(property.ValueNode, navigator, tokens); } - - return tokensFromIEnumerable.ToArray(); + tokens.Add(JsonToken.ObjectEnd()); } private sealed class MaterializationFailedToMatchException : Exception diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonRoundtripTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonRoundtripTests.cs index 78aa476735..fcd55b21f1 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonRoundtripTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonRoundtripTests.cs @@ -614,7 +614,9 @@ private static void MultiSerializationRoundTrip(JsonToken[] inputTokens, string { SerializationSpec.Text(JsonWriteOptions.None), SerializationSpec.Binary(JsonWriteOptions.None), - SerializationSpec.Binary(JsonWriteOptions.EnableNumberArrays | JsonWriteOptions.EnableUInt64Values), + SerializationSpec.Binary(JsonWriteOptions.EnableNumberArrays), + SerializationSpec.Binary(JsonWriteOptions.EnableBase64Strings), + SerializationSpec.Binary(JsonWriteOptions.EnableUInt64Values), SerializationSpec.Binary(JsonWriteOptions.None, userStringEncoded: true), }; @@ -703,7 +705,8 @@ private static void MultiSerializationRoundTrip( Console.WriteLine($" Execution Time (ms): {roundTripResult.ExecutionTime,5}"); Console.WriteLine($" Verification Time (ms): {roundTripResult.VerificationTime,5}"); - strictComparison = !expectRefStringDiffs || (outputSpec.SerializationFormat != JsonSerializationFormat.Binary); + strictComparison = !(expectRefStringDiffs && outputSpec.IsBinary) && + !(inputSpec.IsBinary && !inputSpec.EnablesBase64Strings && outputSpec.IsBinary && outputSpec.EnablesBase64Strings); } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonTestUtils.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonTestUtils.cs index 5203355f74..f2cfbed03f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonTestUtils.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonTestUtils.cs @@ -482,6 +482,8 @@ public static RoundTripResult VerifyJsonRoundTrip( Console.WriteLine($" Output Write Options : {outputSpec.WriteOptions}"); Console.WriteLine($" Input User String Encoding Enabled : {inputSpec.UserStringEncoded}"); Console.WriteLine(); + Console.WriteLine($" Strict Comparison : {strictComparison}"); + Console.WriteLine(); Console.WriteLine($"Comparison Errors:"); Console.WriteLine(verboseOutput.ToString()); @@ -890,6 +892,14 @@ public string SerializationFormatToString() public JsonWriteOptions WriteOptions { get; } public bool IsNewtonsoft { get; } public bool UserStringEncoded { get; } + + public bool IsBinary => this.SerializationFormat == JsonSerializationFormat.Binary; + public bool IsHybridRow => this.SerializationFormat == JsonSerializationFormat.HybridRow; + public bool IsText => this.SerializationFormat == JsonSerializationFormat.Text; + + public bool EnablesBase64Strings => this.WriteOptions.HasFlag(JsonWriteOptions.EnableBase64Strings); + public bool EnablesNumberArrays => this.WriteOptions.HasFlag(JsonWriteOptions.EnableNumberArrays); + public bool EnablesUInt64Values => this.WriteOptions.HasFlag(JsonWriteOptions.EnableUInt64Values); } public class RoundTripBaseline diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonWriterTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonWriterTests.cs index 8f546ce37d..8c27b386b3 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonWriterTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/JsonWriterTests.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using Microsoft.Azure.Cosmos.Json; + using Microsoft.Azure.Cosmos.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using static Microsoft.Azure.Cosmos.Tests.Json.JsonTestUtils; @@ -630,7 +631,7 @@ public void SystemStringTest() [Owner("mayapainter")] public void UserStringTest() { - IJsonStringDictionary jsonStringDictionary = + IJsonStringDictionary jsonStringDictionary = new JsonStringDictionary(new List { "double", "string", "boolean", "null", "datetime", "spatialPoint", "text" }); int userStringId = 0; @@ -1550,6 +1551,1245 @@ public void ReferenceStringsTest() this.VerifyWriter(tokensToWrite, binaryPayload); } } + + [TestMethod] + [Owner("mayapainter")] + public void Base64StringsTest() + { + // -------------------------------------- + // Base64: Small Length (4 to 36 bytes) + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ArrayStart(), + JsonToken.String("TQ=="), + JsonToken.String("TWE="), + JsonToken.String("TWFu"), + JsonToken.String("T3Blbg=="), + JsonToken.String("T3BlbkE="), + JsonToken.String("T3BlbkFJ"), + JsonToken.String("TGl2ZXJwbw=="), + JsonToken.String("TGl2ZXJwb28="), + JsonToken.String("TGl2ZXJwb29s"), + JsonToken.String("TG9uZG9uIEJyaQ=="), + JsonToken.String("TG9uZG9uIEJyaWQ="), + JsonToken.String("TG9uZG9uIEJyaWRn"), + JsonToken.String("Um9ja3dhbGwgVGV4YQ=="), + JsonToken.String("Um9ja3dhbGwgVGV4YXM="), + JsonToken.String("Um9ja3dhbGwgVGV4YXMg"), + JsonToken.String("VGhlIGJyb3duIGRvZyBqdQ=="), + JsonToken.String("VGhlIGJyb3duIGRvZyBqdW0="), + JsonToken.String("VGhlIGJyb3duIGRvZyBqdW1w"), + JsonToken.String("TWljcm9zb2Z0IEF6dXJlIENsbw=="), + JsonToken.String("TWljcm9zb2Z0IEF6dXJlIENsb3U="), + JsonToken.String("TWljcm9zb2Z0IEF6dXJlIENsb3Vk"), + JsonToken.String("ZHJlYW0gaG9wZSBtb29uIGhvcGUgcw=="), + JsonToken.String("ZHJlYW0gaG9wZSBtb29uIGhvcGUgc3U="), + JsonToken.String("ZHJlYW0gaG9wZSBtb29uIGhvcGUgc3Vu"), + JsonToken.String("ir4fbPMf40FNh58zuCRR0C14HYLSLFxuAw=="), + JsonToken.String("ir4fbPMf40FNh58zuCRR0C14HYLSLFxuAw0="), + JsonToken.String("ir4fbPMf40FNh58zuCRR0C14HYLSLFxuAw06"), + JsonToken.ArrayEnd(), + }; + + string[] expectedText = + { + @"[""TQ=="",""TWE="",""TWFu"",""T3Blbg=="",""T3BlbkE="",""T3BlbkFJ"",""TGl2ZXJwbw=="",""TGl2ZXJwb28="",""TGl2ZXJwb29s"",", + @"""TG9uZG9uIEJyaQ=="",""TG9uZG9uIEJyaWQ="",""TG9uZG9uIEJyaWRn"",""Um9ja3dhbGwgVGV4YQ=="",""Um9ja3dhbGwgVGV4YXM", + @"="",""Um9ja3dhbGwgVGV4YXMg"",""VGhlIGJyb3duIGRvZyBqdQ=="",""VGhlIGJyb3duIGRvZyBqdW0="",""VGhlIGJyb3duIGRvZyB", + @"qdW1w"",""TWljcm9zb2Z0IEF6dXJlIENsbw=="",""TWljcm9zb2Z0IEF6dXJlIENsb3U="",""TWljcm9zb2Z0IEF6dXJlIENsb3Vk"",", + @"""ZHJlYW0gaG9wZSBtb29uIGhvcGUgcw=="",""ZHJlYW0gaG9wZSBtb29uIGhvcGUgc3U="",""ZHJlYW0gaG9wZSBtb29uIGhvcGUgc", + @"3Vu"",""ir4fbPMf40FNh58zuCRR0C14HYLSLFxuAw=="",""ir4fbPMf40FNh58zuCRR0C14HYLSLFxuAw0="",""ir4fbPMf40FNh58z", + @"uCRR0C14HYLSLFxuAw06""]", + }; + + string[] expectedBinary1 = + { + "00000000 80 E6 37 02 1B 00 84 54 51 3D 3D 84 54 57 45 3D", + "00000010 84 54 57 46 75 88 54 33 42 6C 62 67 3D 3D 88 54", + "00000020 33 42 6C 62 6B 45 3D 88 54 33 42 6C 62 6B 46 4A", + "00000030 8C 54 47 6C 32 5A 58 4A 77 62 77 3D 3D 8C 54 47", + "00000040 6C 32 5A 58 4A 77 62 32 38 3D 8C 54 47 6C 32 5A", + "00000050 58 4A 77 62 32 39 73 90 54 47 39 75 5A 47 39 75", + "00000060 49 45 4A 79 61 51 3D 3D 90 54 47 39 75 5A 47 39", + "00000070 75 49 45 4A 79 61 57 51 3D 90 54 47 39 75 5A 47", + "00000080 39 75 49 45 4A 79 61 57 52 6E 94 55 6D 39 6A 61", + "00000090 33 64 68 62 47 77 67 56 47 56 34 59 51 3D 3D 94", + "000000A0 55 6D 39 6A 61 33 64 68 62 47 77 67 56 47 56 34", + "000000B0 59 58 4D 3D 94 55 6D 39 6A 61 33 64 68 62 47 77", + "000000C0 67 56 47 56 34 59 58 4D 67 98 56 47 68 6C 49 47", + "000000D0 4A 79 62 33 64 75 49 47 52 76 5A 79 42 71 64 51", + "000000E0 3D 3D 98 56 47 68 6C 49 47 4A 79 62 33 64 75 49", + "000000F0 47 52 76 5A 79 42 71 64 57 30 3D 98 56 47 68 6C", + "00000100 49 47 4A 79 62 33 64 75 49 47 52 76 5A 79 42 71", + "00000110 64 57 31 77 9C 54 57 6C 6A 63 6D 39 7A 62 32 5A", + "00000120 30 49 45 46 36 64 58 4A 6C 49 45 4E 73 62 77 3D", + "00000130 3D 9C 54 57 6C 6A 63 6D 39 7A 62 32 5A 30 49 45", + "00000140 46 36 64 58 4A 6C 49 45 4E 73 62 33 55 3D 9C 54", + "00000150 57 6C 6A 63 6D 39 7A 62 32 5A 30 49 45 46 36 64", + "00000160 58 4A 6C 49 45 4E 73 62 33 56 6B A0 5A 48 4A 6C", + "00000170 59 57 30 67 61 47 39 77 5A 53 42 74 62 32 39 75", + "00000180 49 47 68 76 63 47 55 67 63 77 3D 3D A0 5A 48 4A", + "00000190 6C 59 57 30 67 61 47 39 77 5A 53 42 74 62 32 39", + "000001A0 75 49 47 68 76 63 47 55 67 63 33 55 3D A0 5A 48", + "000001B0 4A 6C 59 57 30 67 61 47 39 77 5A 53 42 74 62 32", + "000001C0 39 75 49 47 68 76 63 47 55 67 63 33 56 75 A4 69", + "000001D0 72 34 66 62 50 4D 66 34 30 46 4E 68 35 38 7A 75", + "000001E0 43 52 52 30 43 31 34 48 59 4C 53 4C 46 78 75 41", + "000001F0 77 3D 3D A4 69 72 34 66 62 50 4D 66 34 30 46 4E", + "00000200 68 35 38 7A 75 43 52 52 30 43 31 34 48 59 4C 53", + "00000210 4C 46 78 75 41 77 30 3D A4 69 72 34 66 62 50 4D", + "00000220 66 34 30 46 4E 68 35 38 7A 75 43 52 52 30 43 31", + "00000230 34 48 59 4C 53 4C 46 78 75 41 77 30 36" + }; + + string[] expectedBinary2 = + { + "00000000 80 E6 FF 01 1B 00 84 54 51 3D 3D 84 54 57 45 3D", + "00000010 84 54 57 46 75 88 54 33 42 6C 62 67 3D 3D 88 54", + "00000020 33 42 6C 62 6B 45 3D 88 54 33 42 6C 62 6B 46 4A", + "00000030 8C 54 47 6C 32 5A 58 4A 77 62 77 3D 3D 8C 54 47", + "00000040 6C 32 5A 58 4A 77 62 32 38 3D 8C 54 47 6C 32 5A", + "00000050 58 4A 77 62 32 39 73 90 54 47 39 75 5A 47 39 75", + "00000060 49 45 4A 79 61 51 3D 3D 90 54 47 39 75 5A 47 39", + "00000070 75 49 45 4A 79 61 57 51 3D 90 54 47 39 75 5A 47", + "00000080 39 75 49 45 4A 79 61 57 52 6E 94 55 6D 39 6A 61", + "00000090 33 64 68 62 47 77 67 56 47 56 34 59 51 3D 3D 94", + "000000A0 55 6D 39 6A 61 33 64 68 62 47 77 67 56 47 56 34", + "000000B0 59 58 4D 3D 94 55 6D 39 6A 61 33 64 68 62 47 77", + "000000C0 67 56 47 56 34 59 58 4D 67 71 06 02 54 68 65 20", + "000000D0 62 72 6F 77 6E 20 64 6F 67 20 6A 75 71 06 01 54", + "000000E0 68 65 20 62 72 6F 77 6E 20 64 6F 67 20 6A 75 6D", + "000000F0 98 56 47 68 6C 49 47 4A 79 62 33 64 75 49 47 52", + "00000100 76 5A 79 42 71 64 57 31 77 71 07 02 4D 69 63 72", + "00000110 6F 73 6F 66 74 20 41 7A 75 72 65 20 43 6C 6F 71", + "00000120 07 01 4D 69 63 72 6F 73 6F 66 74 20 41 7A 75 72", + "00000130 65 20 43 6C 6F 75 9C 54 57 6C 6A 63 6D 39 7A 62", + "00000140 32 5A 30 49 45 46 36 64 58 4A 6C 49 45 4E 73 62", + "00000150 33 56 6B 71 08 02 64 72 65 61 6D 20 68 6F 70 65", + "00000160 20 6D 6F 6F 6E 20 68 6F 70 65 20 73 71 08 01 64", + "00000170 72 65 61 6D 20 68 6F 70 65 20 6D 6F 6F 6E 20 68", + "00000180 6F 70 65 20 73 75 A0 5A 48 4A 6C 59 57 30 67 61", + "00000190 47 39 77 5A 53 42 74 62 32 39 75 49 47 68 76 63", + "000001A0 47 55 67 63 33 56 75 71 09 02 8A BE 1F 6C F3 1F", + "000001B0 E3 41 4D 87 9F 33 B8 24 51 D0 2D 78 1D 82 D2 2C", + "000001C0 5C 6E 03 71 09 01 8A BE 1F 6C F3 1F E3 41 4D 87", + "000001D0 9F 33 B8 24 51 D0 2D 78 1D 82 D2 2C 5C 6E 03 0D", + "000001E0 A4 69 72 34 66 62 50 4D 66 34 30 46 4E 68 35 38", + "000001F0 7A 75 43 52 52 30 43 31 34 48 59 4C 53 4C 46 78", + "00000200 75 41 77 30 36" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Base64 URL: Small Length (20 to 36 bytes) + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ArrayStart(), + JsonToken.String("ZJZ_zM4oK2kvKhQXzlxi"), + JsonToken.String("hgRyg26Sbn5_YGwaQRo="), + JsonToken.String("bNGd7-Tca1Cmo2J9qA=="), + JsonToken.String("qOZlu3Ocr-8tmGkqkLKJxc04"), + JsonToken.String("KOCBGwtjEr513bB1r_5q0FA="), + JsonToken.String("G1CsCUEqvx13pX4f-qF3VA=="), + JsonToken.String("ibuKLVzkgEnBBaFF42IBOOkcb_wA"), + JsonToken.String("pC3TLU5-EVHSFLBzAO29YUUzs5I="), + JsonToken.String("seiItBOs1nFpsI48wXL4BjO-Gg=="), + JsonToken.String("_Yc2bE4qf54zF6pYh3Lw729v0P9xQW5l"), + JsonToken.String("i2A_Yu-maR9HlvRyNp0Zmr5zU8KrTGo="), + JsonToken.String("_7K8d9X-Ld5yA23i_5yA8x3j_2rVv5P_=="), + JsonToken.String("fc3N3-fJqHh4ueJ5eUo_Ungx5uFzweKnyifK"), + JsonToken.String("Lpnrursx4ZEmEV10E2YmHaR8Bk_ICZjV6dA="), + JsonToken.String("Lw3pggK6Zbnh3vDvRSyITbim-5Ku8W8GNQ=="), + JsonToken.ArrayEnd(), + }; + + string[] expectedText = + { + @"[""ZJZ_zM4oK2kvKhQXzlxi"",""hgRyg26Sbn5_YGwaQRo="",""bNGd7-Tca1Cmo2J9qA=="",""qOZlu3Ocr-8tmGkqkLKJxc04"",""KO", + @"CBGwtjEr513bB1r_5q0FA="",""G1CsCUEqvx13pX4f-qF3VA=="",""ibuKLVzkgEnBBaFF42IBOOkcb_wA"",""pC3TLU5-EVHSFLBzA", + @"O29YUUzs5I="",""seiItBOs1nFpsI48wXL4BjO-Gg=="",""_Yc2bE4qf54zF6pYh3Lw729v0P9xQW5l"",""i2A_Yu-maR9HlvRyNp0Z", + @"mr5zU8KrTGo="",""_7K8d9X-Ld5yA23i_5yA8x3j_2rVv5P_=="",""fc3N3-fJqHh4ueJ5eUo_Ungx5uFzweKnyifK"",""Lpnrursx4", + @"ZEmEV10E2YmHaR8Bk_ICZjV6dA="",""Lw3pggK6Zbnh3vDvRSyITbim-5Ku8W8GNQ==""]" + }; + + string[] expectedBinary1 = + { + "00000000 80 E3 B5 01 94 5A 4A 5A 5F 7A 4D 34 6F 4B 32 6B", + "00000010 76 4B 68 51 58 7A 6C 78 69 94 68 67 52 79 67 32", + "00000020 36 53 62 6E 35 5F 59 47 77 61 51 52 6F 3D 94 62", + "00000030 4E 47 64 37 2D 54 63 61 31 43 6D 6F 32 4A 39 71", + "00000040 41 3D 3D 98 71 4F 5A 6C 75 33 4F 63 72 2D 38 74", + "00000050 6D 47 6B 71 6B 4C 4B 4A 78 63 30 34 98 4B 4F 43", + "00000060 42 47 77 74 6A 45 72 35 31 33 62 42 31 72 5F 35", + "00000070 71 30 46 41 3D 98 47 31 43 73 43 55 45 71 76 78", + "00000080 31 33 70 58 34 66 2D 71 46 33 56 41 3D 3D 9C 69", + "00000090 62 75 4B 4C 56 7A 6B 67 45 6E 42 42 61 46 46 34", + "000000A0 32 49 42 4F 4F 6B 63 62 5F 77 41 9C 70 43 33 54", + "000000B0 4C 55 35 2D 45 56 48 53 46 4C 42 7A 41 4F 32 39", + "000000C0 59 55 55 7A 73 35 49 3D 9C 73 65 69 49 74 42 4F", + "000000D0 73 31 6E 46 70 73 49 34 38 77 58 4C 34 42 6A 4F", + "000000E0 2D 47 67 3D 3D A0 5F 59 63 32 62 45 34 71 66 35", + "000000F0 34 7A 46 36 70 59 68 33 4C 77 37 32 39 76 30 50", + "00000100 39 78 51 57 35 6C A0 69 32 41 5F 59 75 2D 6D 61", + "00000110 52 39 48 6C 76 52 79 4E 70 30 5A 6D 72 35 7A 55", + "00000120 38 4B 72 54 47 6F 3D A2 5F 37 4B 38 64 39 58 2D", + "00000130 4C 64 35 79 41 32 33 69 5F 35 79 41 38 78 33 6A", + "00000140 5F 32 72 56 76 35 50 5F 3D 3D A4 66 63 33 4E 33", + "00000150 2D 66 4A 71 48 68 34 75 65 4A 35 65 55 6F 5F 55", + "00000160 6E 67 78 35 75 46 7A 77 65 4B 6E 79 69 66 4B A4", + "00000170 4C 70 6E 72 75 72 73 78 34 5A 45 6D 45 56 31 30", + "00000180 45 32 59 6D 48 61 52 38 42 6B 5F 49 43 5A 6A 56", + "00000190 36 64 41 3D A4 4C 77 33 70 67 67 4B 36 5A 62 6E", + "000001A0 68 33 76 44 76 52 53 79 49 54 62 69 6D 2D 35 4B", + "000001B0 75 38 57 38 47 4E 51 3D 3D" + }; + + string[] expectedBinary2 = + { + "00000000 80 E3 85 01 94 5A 4A 5A 5F 7A 4D 34 6F 4B 32 6B", + "00000010 76 4B 68 51 58 7A 6C 78 69 94 68 67 52 79 67 32", + "00000020 36 53 62 6E 35 5F 59 47 77 61 51 52 6F 3D 94 62", + "00000030 4E 47 64 37 2D 54 63 61 31 43 6D 6F 32 4A 39 71", + "00000040 41 3D 3D 98 71 4F 5A 6C 75 33 4F 63 72 2D 38 74", + "00000050 6D 47 6B 71 6B 4C 4B 4A 78 63 30 34 73 06 01 28", + "00000060 E0 81 1B 0B 63 12 BE 75 DD B0 75 AF FE 6A D0 50", + "00000070 73 06 02 1B 50 AC 09 41 2A BF 1D 77 A5 7E 1F FA", + "00000080 A1 77 54 9C 69 62 75 4B 4C 56 7A 6B 67 45 6E 42", + "00000090 42 61 46 46 34 32 49 42 4F 4F 6B 63 62 5F 77 41", + "000000A0 73 07 01 A4 2D D3 2D 4E 7E 11 51 D2 14 B0 73 00", + "000000B0 ED BD 61 45 33 B3 92 73 07 02 B1 E8 88 B4 13 AC", + "000000C0 D6 71 69 B0 8E 3C C1 72 F8 06 33 BE 1A A0 5F 59", + "000000D0 63 32 62 45 34 71 66 35 34 7A 46 36 70 59 68 33", + "000000E0 4C 77 37 32 39 76 30 50 39 78 51 57 35 6C 73 08", + "000000F0 01 8B 60 3F 62 EF A6 69 1F 47 96 F4 72 36 9D 19", + "00000100 9A BE 73 53 C2 AB 4C 6A A2 5F 37 4B 38 64 39 58", + "00000110 2D 4C 64 35 79 41 32 33 69 5F 35 79 41 38 78 33", + "00000120 6A 5F 32 72 56 76 35 50 5F 3D 3D A4 66 63 33 4E", + "00000130 33 2D 66 4A 71 48 68 34 75 65 4A 35 65 55 6F 5F", + "00000140 55 6E 67 78 35 75 46 7A 77 65 4B 6E 79 69 66 4B", + "00000150 73 09 01 2E 99 EB BA BB 31 E1 91 26 11 5D 74 13", + "00000160 66 26 1D A4 7C 06 4F C8 09 98 D5 E9 D0 73 09 02", + "00000170 2F 0D E9 82 02 BA 65 B9 E1 DE F0 EF 45 2C 88 4D", + "00000180 B8 A6 FB 92 AE F1 6F 06 35" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Base64: Medium Length (100 to 256 bytes) + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ArrayStart(), + JsonToken.String("EbyPEDnfimRJnmBJYW7Iq6Gl1wYzV760XwvpBa6fVbR52O8YobNkdf+A3q10fhbrMe5Moxw3fmn6BgW4JYg2acfC1znUZ5IvThoT"), + JsonToken.String("gAAAAABnkrrmWjhg614TGDpZKHblrTxUMtXPPoaOQdJke7QQN5orn1+hOJt5UPK65sNflmKJRqnkqKzAWMiPBDQxTMCa9uHQ6skp574NXqwonY5IssV5LWsFT+vk6bLeXLehrrA0VPIHjK1LKhTuIm4mymkhvHhA3g=="), + JsonToken.String("tgrstY0naYHJDU+8Xjf6PUFVl4GKtC+Un2e7OuaFKc7+I2D7Q1OAtnPVpfQzpwkPm5+Pa19gCn0H7w38+L73EzsbKDuYiAFGQ8SRo/k3MvAljfOvviKCoVKjM0AKyYdsWL5r69ufoTPAjHwJhDw+zQ16y6EN90VnZnkQAmAiQ4tAg3qFSf9ynv7Pk2bZsbMmoFMH7bI="), + JsonToken.String("WTXMDL2XdMOaRnNRNeAlaw8jsUcaEPBxJvSC47SEz2v3i1FRkNvvxbDFXtEThuhuqkXLjpZw9dIO4Oq/xIc3JcqE/JE87POOxrG35PH6wBCpa0LvRDQITI/S7/zOjX1EqoI4Ku/l58l7mfVqAa3rquaqKzwd3YYitB2ikTHGuAQ7oIu2e8bV60OjtmDZoCdGsdyforPa7YjK5MIt/Rv31bWh9CimjyNGzzCXzhDK96h/oeWsjuJVXUOS+Ujefg=="), + JsonToken.ArrayEnd(), + }; + + string[] expectedText = + { + @"[""EbyPEDnfimRJnmBJYW7Iq6Gl1wYzV760XwvpBa6fVbR52O8YobNkdf+A3q10fhbrMe5Moxw3fmn6BgW4JYg2acfC1znUZ5IvTh", + @"oT"",""gAAAAABnkrrmWjhg614TGDpZKHblrTxUMtXPPoaOQdJke7QQN5orn1+hOJt5UPK65sNflmKJRqnkqKzAWMiPBDQxTMCa9uH", + @"Q6skp574NXqwonY5IssV5LWsFT+vk6bLeXLehrrA0VPIHjK1LKhTuIm4mymkhvHhA3g=="",""tgrstY0naYHJDU+8Xjf6PUFVl4GK", + @"tC+Un2e7OuaFKc7+I2D7Q1OAtnPVpfQzpwkPm5+Pa19gCn0H7w38+L73EzsbKDuYiAFGQ8SRo/k3MvAljfOvviKCoVKjM0AKyYds", + @"WL5r69ufoTPAjHwJhDw+zQ16y6EN90VnZnkQAmAiQ4tAg3qFSf9ynv7Pk2bZsbMmoFMH7bI="",""WTXMDL2XdMOaRnNRNeAlaw8js", + @"UcaEPBxJvSC47SEz2v3i1FRkNvvxbDFXtEThuhuqkXLjpZw9dIO4Oq/xIc3JcqE/JE87POOxrG35PH6wBCpa0LvRDQITI/S7/zOj", + @"X1EqoI4Ku/l58l7mfVqAa3rquaqKzwd3YYitB2ikTHGuAQ7oIu2e8bV60OjtmDZoCdGsdyforPa7YjK5MIt/Rv31bWh9CimjyNGz", + @"zCXzhDK96h/oeWsjuJVXUOS+Ujefg==""]" + }; + + string[] expectedBinary1 = + { + "00000000 80 E3 80 02 7E 64 45 71 1E 5A 24 BA CD E9 B6 54", + "00000010 E9 6E 0B 95 D9 EB 2D 19 B7 1D D9 B1 7B 56 6F BD", + "00000020 D9 60 D8 BB 1D 2E 0C DB CC 56 B1 B4 26 7B E2 B2", + "00000030 6F B1 73 4D 36 AF 82 B3 78 0C 66 46 8B E5 CD 72", + "00000040 AD F9 C6 DF 67 E6 B6 DB 26 3C 5F 69 CA EC 59 16", + "00000050 1E 9B 87 31 BD BB AA AD 25 ED 54 F4 9B 0A 7E A4", + "00000060 E7 60 30 18 0C 0A DD 6B B9 BC 7D 55 A3 CF B6 18", + "00000070 8D 7A 24 C2 B5 4B A4 98 2D A7 E2 AB 4D 3A 16 0A", + "00000080 7D 87 9F 51 B2 72 5D BE 45 A3 CE DA 5B EE 8E AD", + "00000090 D0 4F 25 BD 56 85 2E 6D B5 B9 D3 CC 6E 2F 95 D2", + "000000A0 B8 7B 1D 5F EA 83 D7 66 1A 2A 24 46 F1 D4 E6 30", + "000000B0 9C AB 23 A3 B6 F9 1A 5E BB D1 9C D8 F8 FD ED CE", + "000000C0 D6 92 F3 B9 B5 C6 BC CE 8D D4 95 7D 6D 13 33 CB", + "000000D0 58 66 19 2D 97 07 61 56 68 12 A9 5E C6 98 4B 34", + "000000E0 B5 9E 6C D3 DA F9 F6 1A 6D 47 A2 83 B3 73 AF 07", + "000000F0 7E C8 F4 B3 7C 4E CF C2 DC E1 2C 52 49 AC AE 70", + "00000100 58 B5 D9 06 AD 1A AD 6C DA 71 49 1F AE AA 6E 59", + "00000110 F9 F6 AC 87 8D CB F1 6D 95 94 11 6F D1 D8 33 48", + "00000120 77 43 AD 70 73 54 0F BF AF A1 ED DA 0A 1A 8E E5", + "00000130 CE 43 37 0C 79 BB CF 70 2B E6 6D 56 D4 CF C5 4B", + "00000140 62 3D 9B 0E 1A 8F 51 DC 54 FA 7E AD 67 4D 7B 90", + "00000150 AD 36 3F ED F6 F4 72 F8 B6 2E D5 4D 58 70 99 CF", + "00000160 92 E7 57 66 4D 6E CB D5 CD 6F 2A 34 A8 46 DE 95", + "00000170 68 E2 7D A5 8F C6 6C 79 5B D1 99 83 59 DD 5A F7", + "00000180 3A 1A 6C 07 D3 51 1A 3D 78 9E C5 8D 53 73 2E EF", + "00000190 B6 DF A0 6B 99 58 3B 17 37 DB 6F 63 13 79 13 27", + "000001A0 7B 7F 00 01 57 2A B6 49 64 CA B0 E4 E6 33 2C 75", + "000001B0 3B A5 CE 72 90 1D BE E3 D4 F3 EA 38 5C 84 0A F1", + "000001C0 4A FB 74 48 BB 4D 8B 7A 99 7D 96 8E 19 A5 6B A7", + "000001D0 DD 8E 17 13 8D 58 7A 91 8A AE A3 EB F1 35 96 A9", + "000001E0 86 6B EF 39 72 F2 49 7B C6 5F F8 E4 78 A6 1C C7", + "000001F0 8B 2F 65 11 77 83 3E 9F 78 F9 71 56 83 22 6D 77", + "00000200 E1 10 1E 86 31 ED 52 62 34 49 4D BE A6 B7 97 FE", + "00000210 A9 C6 C6 8A F1 77 92 B6 AC BF D8 35 1C FB D6 36", + "00000220 5B E3 C1 F0 4C 1E AF 87 E3 4B FD 9D 3C CB 66 D3", + "00000230 74 A1 2C BD A6 22 8F F5 60 F4 F6 4E D6 65 65 9C", + "00000240 D8 6A 83 3D D5 F4 36 51 FB 1E 92 8F 73 72 DE FC", + "00000250 96 43 C3 B7 AC 7A 59 6B 26 E9 2F A9 7D 16 13 5F", + "00000260 D1 B9 61 BA AD CE 3B 8F 7A FD 10 AB 47 13 97 39", + "00000270 1B FA F5 2E 5F E7 EA BA D2 8A AD 3E A7 AB AA BA", + "00000280 6C 3E F7 7A" + }; + + string[] expectedBinary2 = + { + "00000000 80 E3 23 02 71 19 00 11 BC 8F 10 39 DF 8A 64 49", + "00000010 9E 60 49 61 6E C8 AB A1 A5 D7 06 33 57 BE B4 5F", + "00000020 0B E9 05 AE 9F 55 B4 79 D8 EF 18 A1 B3 64 75 FF", + "00000030 80 DE AD 74 7E 16 EB 31 EE 4C A3 1C 37 7E 69 FA", + "00000040 06 05 B8 25 88 36 69 C7 C2 D7 39 D4 67 92 2F 4E", + "00000050 1A 13 71 29 02 80 00 00 00 00 67 92 BA E6 5A 38", + "00000060 60 EB 5E 13 18 3A 59 28 76 E5 AD 3C 54 32 D5 CF", + "00000070 3E 86 8E 41 D2 64 7B B4 10 37 9A 2B 9F 5F A1 38", + "00000080 9B 79 50 F2 BA E6 C3 5F 96 62 89 46 A9 E4 A8 AC", + "00000090 C0 58 C8 8F 04 34 31 4C C0 9A F6 E1 D0 EA C9 29", + "000000A0 E7 BE 0D 5E AC 28 9D 8E 48 B2 C5 79 2D 6B 05 4F", + "000000B0 EB E4 E9 B2 DE 5C B7 A1 AE B0 34 54 F2 07 8C AD", + "000000C0 4B 2A 14 EE 22 6E 26 CA 69 21 BC 78 40 DE 71 32", + "000000D0 01 B6 0A EC B5 8D 27 69 81 C9 0D 4F BC 5E 37 FA", + "000000E0 3D 41 55 97 81 8A B4 2F 94 9F 67 BB 3A E6 85 29", + "000000F0 CE FE 23 60 FB 43 53 80 B6 73 D5 A5 F4 33 A7 09", + "00000100 0F 9B 9F 8F 6B 5F 60 0A 7D 07 EF 0D FC F8 BE F7", + "00000110 13 3B 1B 28 3B 98 88 01 46 43 C4 91 A3 F9 37 32", + "00000120 F0 25 8D F3 AF BE 22 82 A1 52 A3 33 40 0A C9 87", + "00000130 6C 58 BE 6B EB DB 9F A1 33 C0 8C 7C 09 84 3C 3E", + "00000140 CD 0D 7A CB A1 0D F7 45 67 66 79 10 02 60 22 43", + "00000150 8B 40 83 7A 85 49 FF 72 9E FE CF 93 66 D9 B1 B3", + "00000160 26 A0 53 07 ED B2 71 40 02 59 35 CC 0C BD 97 74", + "00000170 C3 9A 46 73 51 35 E0 25 6B 0F 23 B1 47 1A 10 F0", + "00000180 71 26 F4 82 E3 B4 84 CF 6B F7 8B 51 51 90 DB EF", + "00000190 C5 B0 C5 5E D1 13 86 E8 6E AA 45 CB 8E 96 70 F5", + "000001A0 D2 0E E0 EA BF C4 87 37 25 CA 84 FC 91 3C EC F3", + "000001B0 8E C6 B1 B7 E4 F1 FA C0 10 A9 6B 42 EF 44 34 08", + "000001C0 4C 8F D2 EF FC CE 8D 7D 44 AA 82 38 2A EF E5 E7", + "000001D0 C9 7B 99 F5 6A 01 AD EB AA E6 AA 2B 3C 1D DD 86", + "000001E0 22 B4 1D A2 91 31 C6 B8 04 3B A0 8B B6 7B C6 D5", + "000001F0 EB 43 A3 B6 60 D9 A0 27 46 B1 DC 9F A2 B3 DA ED", + "00000200 88 CA E4 C2 2D FD 1B F7 D5 B5 A1 F4 28 A6 8F 23", + "00000210 46 CF 30 97 CE 10 CA F7 A8 7F A1 E5 AC 8E E2 55", + "00000220 5D 43 92 F9 48 DE 7E" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Base64 URL: Medium Length (100 to 256 bytes) + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ArrayStart(), + JsonToken.String("EbyPEDnfimRJnmBJYW7Iq6Gl1wYzV760XwvpBa6fVbR52O8YobNkdf-A3q10fhbrMe5Moxw3fmn6BgW4JYg2acfC1znUZ5IvThoT"), + JsonToken.String("gAAAAABnkrrmWjhg614TGDpZKHblrTxUMtXPPoaOQdJke7QQN5orn1-hOJt5UPK65sNflmKJRqnkqKzAWMiPBDQxTMCa9uHQ6skp574NXqwonY5IssV5LWsFT-vk6bLeXLehrrA0VPIHjK1LKhTuIm4mymkhvHhA3g=="), + JsonToken.String("tgrstY0naYHJDU-8Xjf6PUFVl4GKtC-Un2e7OuaFKc7-I2D7Q1OAtnPVpfQzpwkPm5-Pa19gCn0H7w38-L73EzsbKDuYiAFGQ8SRo_k3MvAljfOvviKCoVKjM0AKyYdsWL5r69ufoTPAjHwJhDw-zQ16y6EN90VnZnkQAmAiQ4tAg3qFSf9ynv7Pk2bZsbMmoFMH7bI="), + JsonToken.String("WTXMDL2XdMOaRnNRNeAlaw8jsUcaEPBxJvSC47SEz2v3i1FRkNvvxbDFXtEThuhuqkXLjpZw9dIO4Oq_xIc3JcqE_JE87POOxrG35PH6wBCpa0LvRDQITI_S7_zOjX1EqoI4Ku_l58l7mfVqAa3rquaqKzwd3YYitB2ikTHGuAQ7oIu2e8bV60OjtmDZoCdGsdyforPa7YjK5MIt_Rv31bWh9CimjyNGzzCXzhDK96h_oeWsjuJVXUOS-Ujefg=="), + JsonToken.ArrayEnd(), + }; + + string[] expectedText = + { + @"[""EbyPEDnfimRJnmBJYW7Iq6Gl1wYzV760XwvpBa6fVbR52O8YobNkdf-A3q10fhbrMe5Moxw3fmn6BgW4JYg2acfC1znUZ5IvTh", + @"oT"",""gAAAAABnkrrmWjhg614TGDpZKHblrTxUMtXPPoaOQdJke7QQN5orn1-hOJt5UPK65sNflmKJRqnkqKzAWMiPBDQxTMCa9uH", + @"Q6skp574NXqwonY5IssV5LWsFT-vk6bLeXLehrrA0VPIHjK1LKhTuIm4mymkhvHhA3g=="",""tgrstY0naYHJDU-8Xjf6PUFVl4GK", + @"tC-Un2e7OuaFKc7-I2D7Q1OAtnPVpfQzpwkPm5-Pa19gCn0H7w38-L73EzsbKDuYiAFGQ8SRo_k3MvAljfOvviKCoVKjM0AKyYds", + @"WL5r69ufoTPAjHwJhDw-zQ16y6EN90VnZnkQAmAiQ4tAg3qFSf9ynv7Pk2bZsbMmoFMH7bI="",""WTXMDL2XdMOaRnNRNeAlaw8js", + @"UcaEPBxJvSC47SEz2v3i1FRkNvvxbDFXtEThuhuqkXLjpZw9dIO4Oq_xIc3JcqE_JE87POOxrG35PH6wBCpa0LvRDQITI_S7_zOj", + @"X1EqoI4Ku_l58l7mfVqAa3rquaqKzwd3YYitB2ikTHGuAQ7oIu2e8bV60OjtmDZoCdGsdyforPa7YjK5MIt_Rv31bWh9CimjyNGz", + @"zCXzhDK96h_oeWsjuJVXUOS-Ujefg==""]" + }; + + string[] expectedBinary1 = + { + "00000000 80 E3 80 02 7E 64 45 71 1E 5A 24 BA CD E9 B6 54", + "00000010 E9 6E 0B 95 D9 EB 2D 19 B7 1D D9 B1 7B 56 6F BD", + "00000020 D9 60 D8 BB 1D 2E 0C DB CC 56 B1 B4 26 7B E2 B2", + "00000030 6F B1 73 4D 36 B7 82 B3 78 0C 66 46 8B E5 CD 72", + "00000040 AD F9 C6 DF 67 E6 B6 DB 26 3C 5F 69 CA EC 59 16", + "00000050 1E 9B 87 31 BD BB AA AD 25 ED 54 F4 9B 0A 7E A4", + "00000060 E7 60 30 18 0C 0A DD 6B B9 BC 7D 55 A3 CF B6 18", + "00000070 8D 7A 24 C2 B5 4B A4 98 2D A7 E2 AB 4D 3A 16 0A", + "00000080 7D 87 9F 51 B2 72 5D BE 45 A3 CE DA 5B EE 8E B5", + "00000090 D0 4F 25 BD 56 85 2E 6D B5 B9 D3 CC 6E 2F 95 D2", + "000000A0 B8 7B 1D 5F EA 83 D7 66 1A 2A 24 46 F1 D4 E6 30", + "000000B0 9C AB 23 A3 B6 F9 1A 5E BB D1 9C D8 F8 FD ED CE", + "000000C0 D6 92 F3 B9 B5 C6 BC CE 8D D4 96 7D 6D 13 33 CB", + "000000D0 58 66 19 2D 97 07 61 56 68 12 A9 5E C6 98 4B 34", + "000000E0 B5 9E 6C D3 DA F9 F6 1A 6D 47 A2 83 B3 73 AF 07", + "000000F0 7E C8 F4 B3 7C 4E CF C2 DC E1 2C 52 49 AC B6 70", + "00000100 58 B5 D9 06 AD 1A AD 6C DA 71 49 1F B6 AA 6E 59", + "00000110 F9 F6 AC 87 8D CB F1 AD 95 94 11 6F D1 D8 33 48", + "00000120 77 43 AD 70 73 54 0F BF AF A1 ED 5A 0B 1A 8E E5", + "00000130 CE 43 37 0C 79 BB CF 70 2D E6 6D 56 D4 CF C5 4B", + "00000140 62 3D 9B 0E 1A 8F 51 DC 54 FA FE AE 67 4D 7B 90", + "00000150 AD 36 3F ED F6 F4 72 F8 B6 2E D5 4D 58 70 99 CF", + "00000160 92 E7 57 66 4D 6E CB D5 CD 6F 2A 34 A8 46 DE 95", + "00000170 68 E2 BD A5 8F C6 6C 79 5B D1 99 83 59 DD 5A F7", + "00000180 3A 1A 6C 07 D3 51 1A 3D 78 9E C5 8D 53 73 2E EF", + "00000190 B6 DF A0 6B 99 58 3B 17 37 DB 6F 63 13 79 13 27", + "000001A0 7B 7F 00 01 57 2A B6 49 64 CA B0 E4 E6 33 2C 75", + "000001B0 3B A5 CE 72 90 1D BE E3 D4 F3 EA 38 5C 84 0A F1", + "000001C0 4A FB 74 48 BB 4D 8B 7A 99 7D 96 8E 19 A5 6B A7", + "000001D0 DD 8E 17 13 8D 58 7A 91 8A AE A3 EB F1 35 96 A9", + "000001E0 86 6B EF 39 72 F2 49 7B C6 BF F8 E4 78 A6 1C C7", + "000001F0 8B 5F 65 11 77 83 3E 9F 78 F9 71 56 83 22 6D 77", + "00000200 E1 10 1E 86 31 ED 52 62 34 49 4D 7E A7 B7 AF FE", + "00000210 A9 C6 C6 8A F1 77 92 B6 AC 7F D9 35 1C FB D6 36", + "00000220 5B E3 C1 F0 4C 1E AF 87 E3 4B FD 9D 3C CB 66 D3", + "00000230 74 A1 2C BD A6 22 8F F5 60 F4 F6 4E D6 65 65 9C", + "00000240 D8 6A 83 3D D5 F4 36 51 FB 1E 92 8F 73 72 DE FC", + "00000250 96 43 C3 B7 AC 7A 59 6B 26 E9 5F A9 7D 16 13 5F", + "00000260 D1 B9 61 BA AD CE 3B 8F 7A FD 10 AB 47 13 97 39", + "00000270 1B FA FB 2E 5F E7 EA BA D2 8A AD 3E A7 AD AA BA", + "00000280 6C 3E F7 7A" + }; + + string[] expectedBinary2 = + { + "00000000 80 E3 23 02 73 19 00 11 BC 8F 10 39 DF 8A 64 49", + "00000010 9E 60 49 61 6E C8 AB A1 A5 D7 06 33 57 BE B4 5F", + "00000020 0B E9 05 AE 9F 55 B4 79 D8 EF 18 A1 B3 64 75 FF", + "00000030 80 DE AD 74 7E 16 EB 31 EE 4C A3 1C 37 7E 69 FA", + "00000040 06 05 B8 25 88 36 69 C7 C2 D7 39 D4 67 92 2F 4E", + "00000050 1A 13 73 29 02 80 00 00 00 00 67 92 BA E6 5A 38", + "00000060 60 EB 5E 13 18 3A 59 28 76 E5 AD 3C 54 32 D5 CF", + "00000070 3E 86 8E 41 D2 64 7B B4 10 37 9A 2B 9F 5F A1 38", + "00000080 9B 79 50 F2 BA E6 C3 5F 96 62 89 46 A9 E4 A8 AC", + "00000090 C0 58 C8 8F 04 34 31 4C C0 9A F6 E1 D0 EA C9 29", + "000000A0 E7 BE 0D 5E AC 28 9D 8E 48 B2 C5 79 2D 6B 05 4F", + "000000B0 EB E4 E9 B2 DE 5C B7 A1 AE B0 34 54 F2 07 8C AD", + "000000C0 4B 2A 14 EE 22 6E 26 CA 69 21 BC 78 40 DE 73 32", + "000000D0 01 B6 0A EC B5 8D 27 69 81 C9 0D 4F BC 5E 37 FA", + "000000E0 3D 41 55 97 81 8A B4 2F 94 9F 67 BB 3A E6 85 29", + "000000F0 CE FE 23 60 FB 43 53 80 B6 73 D5 A5 F4 33 A7 09", + "00000100 0F 9B 9F 8F 6B 5F 60 0A 7D 07 EF 0D FC F8 BE F7", + "00000110 13 3B 1B 28 3B 98 88 01 46 43 C4 91 A3 F9 37 32", + "00000120 F0 25 8D F3 AF BE 22 82 A1 52 A3 33 40 0A C9 87", + "00000130 6C 58 BE 6B EB DB 9F A1 33 C0 8C 7C 09 84 3C 3E", + "00000140 CD 0D 7A CB A1 0D F7 45 67 66 79 10 02 60 22 43", + "00000150 8B 40 83 7A 85 49 FF 72 9E FE CF 93 66 D9 B1 B3", + "00000160 26 A0 53 07 ED B2 73 40 02 59 35 CC 0C BD 97 74", + "00000170 C3 9A 46 73 51 35 E0 25 6B 0F 23 B1 47 1A 10 F0", + "00000180 71 26 F4 82 E3 B4 84 CF 6B F7 8B 51 51 90 DB EF", + "00000190 C5 B0 C5 5E D1 13 86 E8 6E AA 45 CB 8E 96 70 F5", + "000001A0 D2 0E E0 EA BF C4 87 37 25 CA 84 FC 91 3C EC F3", + "000001B0 8E C6 B1 B7 E4 F1 FA C0 10 A9 6B 42 EF 44 34 08", + "000001C0 4C 8F D2 EF FC CE 8D 7D 44 AA 82 38 2A EF E5 E7", + "000001D0 C9 7B 99 F5 6A 01 AD EB AA E6 AA 2B 3C 1D DD 86", + "000001E0 22 B4 1D A2 91 31 C6 B8 04 3B A0 8B B6 7B C6 D5", + "000001F0 EB 43 A3 B6 60 D9 A0 27 46 B1 DC 9F A2 B3 DA ED", + "00000200 88 CA E4 C2 2D FD 1B F7 D5 B5 A1 F4 28 A6 8F 23", + "00000210 46 CF 30 97 CE 10 CA F7 A8 7F A1 E5 AC 8E E2 55", + "00000220 5D 43 92 F9 48 DE 7E" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Base64: Large Length (1200 bytes) + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.String( + "2hhLDu+G0Ca3khg9lR4cjTF3gwDffhdQ8dC8lzXzV9l6I/ahWqUwNc0Deqobc3pjqVIpujXVFLQc5r64k4oj0s8PhsbFeNsnqJ43SXRG3e/iYTNEIflgVyLT" + + "c6Z5SQq8KvKapY0R+bgg8GgoUcIKIxcgmOds1MTN+deCcrNUkFgz5pelkIGfVxZEFZVWvsG6osL7pMMBzA/YSRhctJyX1Sx3f6921S1ySlWjpIbLH/nAthBw" + + "mCuZ38nPu7vU7yWDKHIu3KhfQOqV03Ld1OSgkjCSZEZGfkImWP4ZaA1OpU40Jnf2SZf8By/VdOx0/bMS1rMw6rOlNQiDzmG438fSKureqDyFLvIi8VpOyWjO" + + "7GBZKTEzGleOxPqOcVzL2l0zGa9wjDGTIP837XVTGF1Dt2iNjG8EjuXtdiZO6xCgTRiPHnF5iL2rl9rX+C/7raMVDKpSwa8H3Pdbya3OsaDsUlqr1P7Z+VC7" + + "Hkxu+cGwrgNVcpClURWHzoz86wt+XvrNJ2WPaDBPIkfRFyvkHPkkhGwxU0nrc0dQBTKWVIRVNpvh//S1WwIYU0l/SuRzNopu0h7729wH9zlUqI/2DlodjIJx" + + "ObBihPOFWWMo7DX5zWK+dtEGTAF3hEPzLQ2FM3ooq03XZuLfgumXfOSnyx3an1D0fgcefn83tecnp5yZtwEigAQsUGuT702xPN8fzpxQcSOJ7TtKYGiQkFqD" + + "RiAxVIeJvxKRBQHZYjVh4g4ESrMDCaEYCy1Fab2tk6MNfSY+tO5CKPZAN8nerbYfc0pUoeJPw39M2R5ws+lPlLx8OSu3RBK8W5Q4Se71Fo6EkTpfIaAXS0go" + + "6luJcbZnLzvlQsJ2RY4llIUhLR8ZJIPfx3bmhX3bpmmnYmZdntv51B61+ckts2ZGIId9dQ3ki4jNQzLRb1ogzdpexzmK7EBttjh5uNvSLQemwIznAEXQsWV8" + + "dIxM6XwGWW0ai8/42aunfOp3wrP/NRd4Z+8uis3M72e/6BuofyRtAssb2enUdGoD0x7OlxTC3mMf3GKIloWN3t8npKWNeQuRfsWmEKLQNmWOd2Pvkh9H0iYN" + + "TeGSHJ2Bs/rRdd7nycHct3IIypDs8EFWIdrJj3Ug/ZgpX8nUNGihNDLYR7wEwNxsinqHmluynDaHahH7/lWWgAxfKRkHg4yelZpY8fBJNnDwjUk/1sXO3g==") + }; + + string[] expectedText = + { + @"""2hhLDu+G0Ca3khg9lR4cjTF3gwDffhdQ8dC8lzXzV9l6I/ahWqUwNc0Deqobc3pjqVIpujXVFLQc5r64k4oj0s8PhsbFeNsnqJ4", + @"3SXRG3e/iYTNEIflgVyLTc6Z5SQq8KvKapY0R+bgg8GgoUcIKIxcgmOds1MTN+deCcrNUkFgz5pelkIGfVxZEFZVWvsG6osL7pMM", + @"BzA/YSRhctJyX1Sx3f6921S1ySlWjpIbLH/nAthBwmCuZ38nPu7vU7yWDKHIu3KhfQOqV03Ld1OSgkjCSZEZGfkImWP4ZaA1OpU4", + @"0Jnf2SZf8By/VdOx0/bMS1rMw6rOlNQiDzmG438fSKureqDyFLvIi8VpOyWjO7GBZKTEzGleOxPqOcVzL2l0zGa9wjDGTIP837XV", + @"TGF1Dt2iNjG8EjuXtdiZO6xCgTRiPHnF5iL2rl9rX+C/7raMVDKpSwa8H3Pdbya3OsaDsUlqr1P7Z+VC7Hkxu+cGwrgNVcpClURW", + @"Hzoz86wt+XvrNJ2WPaDBPIkfRFyvkHPkkhGwxU0nrc0dQBTKWVIRVNpvh//S1WwIYU0l/SuRzNopu0h7729wH9zlUqI/2DlodjIJ", + @"xObBihPOFWWMo7DX5zWK+dtEGTAF3hEPzLQ2FM3ooq03XZuLfgumXfOSnyx3an1D0fgcefn83tecnp5yZtwEigAQsUGuT702xPN8", + @"fzpxQcSOJ7TtKYGiQkFqDRiAxVIeJvxKRBQHZYjVh4g4ESrMDCaEYCy1Fab2tk6MNfSY+tO5CKPZAN8nerbYfc0pUoeJPw39M2R5", + @"ws+lPlLx8OSu3RBK8W5Q4Se71Fo6EkTpfIaAXS0go6luJcbZnLzvlQsJ2RY4llIUhLR8ZJIPfx3bmhX3bpmmnYmZdntv51B61+ck", + @"ts2ZGIId9dQ3ki4jNQzLRb1ogzdpexzmK7EBttjh5uNvSLQemwIznAEXQsWV8dIxM6XwGWW0ai8/42aunfOp3wrP/NRd4Z+8uis3", + @"M72e/6BuofyRtAssb2enUdGoD0x7OlxTC3mMf3GKIloWN3t8npKWNeQuRfsWmEKLQNmWOd2Pvkh9H0iYNTeGSHJ2Bs/rRdd7nycH", + @"ct3IIypDs8EFWIdrJj3Ug/ZgpX8nUNGihNDLYR7wEwNxsinqHmluynDaHahH7/lWWgAxfKRkHg4yelZpY8fBJNnDwjUk/1sXO3g=", + @"=""" + }; + + string[] expectedBinary1 = + { + "00000000 80 7F B0 04 32 34 9A 49 AC AF 8E B0 61 78 B6 46", + "00000010 9F 73 6C 29 6D AC A6 1A 67 E7 3B D1 6C 46 93 A3", + "00000020 38 F2 10 C7 D6 63 F5 D6 1C DB 96 7C 85 D1 D7 78", + "00000030 F5 EE 1C C3 88 E5 F8 5B 3C 9E C1 D5 71 6B 12 5E", + "00000040 57 63 AD 46 66 74 5C 93 DB 68 6B DA 5B 0D 9B E3", + "00000050 A0 E8 B9 D8 58 76 CE DD 71 25 6D 36 C5 4A 8F B3", + "00000060 F2 2B 9D A5 3A 8B 49 33 FB 6C CD 33 A9 63 9B B6", + "00000070 36 8D C6 71 4B FB 32 0C CF C2 A4 2B F1 F9 8C 3B", + "00000080 9E DF D5 71 72 99 C4 8F CF ED 27 79 1E 6B 52 9D", + "00000090 2B 72 79 38 96 3B AB 6B E3 59 5F 83 97 D9 EB E4", + "000000A0 D1 6C C5 6B 8B 46 AD F5 6A 9F 1F 6D EF 39 F3 06", + "000000B0 6F 36 85 FA E0 2B 3B 95 A2 C7 74 65 1E 1B 9B E2", + "000000C0 67 66 5B 4E 16 9B C6 F2 53 F6 55 0D 4F 8A 99 C8", + "000000D0 97 3B 48 47 0B EF ED 61 5D 3B C3 B9 A1 F5 9B BD", + "000000E0 7A CB 5F 89 4B 64 B2 3E 5B A2 CD D1 67 DC 0A 9B", + "000000F0 31 C9 B1 E7 F4 BC 56 0F A7 DA A2 F6 68 5E 27 DB", + "00000100 57 28 4D 1B 0E C6 9E F0 2A 0D A6 74 9B 65 53 AD", + "00000110 19 27 CC BF AC E4 27 1E F6 12 37 A7 31 79 F3 6E", + "00000120 93 3F D9 CE 68 9A A8 6F 1F 69 33 9C 79 BA AC CB", + "00000130 CB 71 62 DE C8 B4 27 D3 38 2B FC 99 BF AA 9F B7", + "00000140 A3 50 BB A4 16 F5 47 76 F9 89 87 C6 9F 63 AB 9E", + "00000150 29 63 C3 F4 C7 70 EE AE 26 1E A9 49 28 6E 76 C3", + "00000160 5A A9 47 63 8C 48 97 A5 9D EA 23 AE A8 AE 63 E9", + "00000170 E4 B4 F6 69 C3 0F CF 54 69 1A 8A 74 1B 6B 69 A6", + "00000180 4C CE CE C9 B1 AB E1 EB 26 0F 37 AD C4 25 7C 7A", + "00000190 0F E3 90 33 28 59 9C 0F CF 9E F3 30 71 5E 65 C7", + "000001A0 E5 31 E8 4D BB B2 0E 6F C8 35 BE BE 1A 1F EF F2", + "000001B0 B3 D3 3A 86 0F D9 55 E9 15 A9 7F EB 71 B6 3B 7D", + "000001C0 85 B5 CB 9D 4A D9 15 1A 26 0A A1 C9 B5 59 6A CC", + "000001D0 DB D7 48 E8 7A 8D 3E DE F1 55 98 5B 3E 86 91 A3", + "000001E0 42 EA F2 6A 4D 4A AD 4E B8 1D FD 7A 4D 63 D7 7B", + "000001F0 32 5B 85 B1 5F D3 BA 54 EF 7C C3 EB 30 F4 ED 26", + "00000200 CB DD 91 39 3D BB 1A 4F BE 64 44 F6 9B AC 4E 2A", + "00000210 F1 4F B1 30 8D 86 3E 8D D7 6B F3 7D 23 62 6B FA", + "00000220 EB 72 45 A6 17 8F D4 A0 71 86 2E 42 F5 CC A8 CC", + "00000230 D8 9C BD DF 71 D8 0C AB AD 33 CD E7 7A 1B 6B 7E", + "00000240 4E DD 79 FC 2C EC 8E 11 61 E6 F3 B8 6C 76 E3 66", + "00000250 F4 F2 D8 0D AF E5 B5 F4 7B 31 7D 0E 46 E7 D5 63", + "00000260 9D 7A 83 C9 F0 50 27 CE AC 87 E3 A3 E3 E9 53 79", + "00000270 A3 D2 97 D9 63 3A BA 36 C6 89 D2 74 10 6F 4D 96", + "00000280 95 76 FC 52 2A 8C 22 B5 59 B5 15 4D 3B D3 8A 53", + "00000290 79 93 38 0C 17 B3 C3 7C CC 18 16 CB E8 6B 5B D3", + "000002A0 69 9E 66 57 F4 67 6D B8 84 6A 83 4E 9C BB 2C 17", + "000002B0 67 CD 63 18 BC FA 2E 2B A1 F7 59 AE 29 93 D6 EE", + "000002C0 F3 15 1B CA 66 E2 71 CF 69 7D 26 15 2E 71 D7 5A", + "000002D0 94 36 2D DF 62 C6 B7 AD B8 A6 C2 CD C9 70 10 3B", + "000002E0 85 9D DF 36 76 5D 39 16 6B DD 4C BD 9D 1D 9D 2B", + "000002F0 65 D2 2C 8D CD 4E 56 D1 4C 29 4E AB 4C 42 CD F8", + "00000300 99 B8 8D C6 CE C4 F0 76 DB 9D 6D 6B C9 6E BA BD", + "00000310 16 13 DA 62 AB F1 9A 3E 97 69 8F C9 24 39 47 8E", + "00000320 CE D6 69 9A DA 19 D5 33 A5 E2 D8 FB AC 27 C3 CB", + "00000330 78 7D 7B 79 2B 0A E9 74 35 BA 56 77 DA A7 CC 68", + "00000340 B9 7D 4F EA DD C1 22 36 3A BF 5A 71 E4 24 BE 69", + "00000350 C3 DE 8F D7 2B 2C 9C C6 BD 68 B2 70 DD 6D 7E C2", + "00000360 67 77 39 F4 E5 94 92 69 DA 15 AE 9E 9E CF 9A 37", + "00000370 59 F9 65 13 D6 DF E6 BC 94 1E 9C CF C5 B2 B2 BB", + "00000380 4A 3E BE 89 30 FC ED C9 C6 53 87 B3 76 D3 3C 3B", + "00000390 2E 93 EC F7 D5 39 A3 E3 DC F0 E5 D5 59 8E D6 A5", + "000003A0 E6 F9 B5 5D 5C 32 A3 CE F6 F5 49 96 41 ED 6B 74", + "000003B0 0E 09 4B 67 9D D4 F2 71 8A 54 CA 84 F3 97 5C 4A", + "000003C0 26 DF DC F9 31 72 4C 9F 25 93 79 38 71 8E 2B 1A", + "000003D0 AF 49 B2 5C A9 9E 55 CF 2F ED 19 8E C5 B9 AB CE", + "000003E0 63 1A ED 24 32 B3 D2 DB BD 78 77 E2 E7 69 77 1C", + "000003F0 D9 66 D7 F3 6E 62 18 19 46 23 6F 2F F6 F5 7A 0E", + "00000400 E2 CD 4B E9 1A 79 A6 E5 CB 6C 2D 3C 8B 33 0B 95", + "00000410 4E 37 F1 AE AE AE 5F B1 39 F6 39 3B F7 7A" + }; + + string[] expectedBinary2 = + { + "00000000 80 72 2C 01 02 DA 18 4B 0E EF 86 D0 26 B7 92 18", + "00000010 3D 95 1E 1C 8D 31 77 83 00 DF 7E 17 50 F1 D0 BC", + "00000020 97 35 F3 57 D9 7A 23 F6 A1 5A A5 30 35 CD 03 7A", + "00000030 AA 1B 73 7A 63 A9 52 29 BA 35 D5 14 B4 1C E6 BE", + "00000040 B8 93 8A 23 D2 CF 0F 86 C6 C5 78 DB 27 A8 9E 37", + "00000050 49 74 46 DD EF E2 61 33 44 21 F9 60 57 22 D3 73", + "00000060 A6 79 49 0A BC 2A F2 9A A5 8D 11 F9 B8 20 F0 68", + "00000070 28 51 C2 0A 23 17 20 98 E7 6C D4 C4 CD F9 D7 82", + "00000080 72 B3 54 90 58 33 E6 97 A5 90 81 9F 57 16 44 15", + "00000090 95 56 BE C1 BA A2 C2 FB A4 C3 01 CC 0F D8 49 18", + "000000A0 5C B4 9C 97 D5 2C 77 7F AF 76 D5 2D 72 4A 55 A3", + "000000B0 A4 86 CB 1F F9 C0 B6 10 70 98 2B 99 DF C9 CF BB", + "000000C0 BB D4 EF 25 83 28 72 2E DC A8 5F 40 EA 95 D3 72", + "000000D0 DD D4 E4 A0 92 30 92 64 46 46 7E 42 26 58 FE 19", + "000000E0 68 0D 4E A5 4E 34 26 77 F6 49 97 FC 07 2F D5 74", + "000000F0 EC 74 FD B3 12 D6 B3 30 EA B3 A5 35 08 83 CE 61", + "00000100 B8 DF C7 D2 2A EA DE A8 3C 85 2E F2 22 F1 5A 4E", + "00000110 C9 68 CE EC 60 59 29 31 33 1A 57 8E C4 FA 8E 71", + "00000120 5C CB DA 5D 33 19 AF 70 8C 31 93 20 FF 37 ED 75", + "00000130 53 18 5D 43 B7 68 8D 8C 6F 04 8E E5 ED 76 26 4E", + "00000140 EB 10 A0 4D 18 8F 1E 71 79 88 BD AB 97 DA D7 F8", + "00000150 2F FB AD A3 15 0C AA 52 C1 AF 07 DC F7 5B C9 AD", + "00000160 CE B1 A0 EC 52 5A AB D4 FE D9 F9 50 BB 1E 4C 6E", + "00000170 F9 C1 B0 AE 03 55 72 90 A5 51 15 87 CE 8C FC EB", + "00000180 0B 7E 5E FA CD 27 65 8F 68 30 4F 22 47 D1 17 2B", + "00000190 E4 1C F9 24 84 6C 31 53 49 EB 73 47 50 05 32 96", + "000001A0 54 84 55 36 9B E1 FF F4 B5 5B 02 18 53 49 7F 4A", + "000001B0 E4 73 36 8A 6E D2 1E FB DB DC 07 F7 39 54 A8 8F", + "000001C0 F6 0E 5A 1D 8C 82 71 39 B0 62 84 F3 85 59 63 28", + "000001D0 EC 35 F9 CD 62 BE 76 D1 06 4C 01 77 84 43 F3 2D", + "000001E0 0D 85 33 7A 28 AB 4D D7 66 E2 DF 82 E9 97 7C E4", + "000001F0 A7 CB 1D DA 9F 50 F4 7E 07 1E 7E 7F 37 B5 E7 27", + "00000200 A7 9C 99 B7 01 22 80 04 2C 50 6B 93 EF 4D B1 3C", + "00000210 DF 1F CE 9C 50 71 23 89 ED 3B 4A 60 68 90 90 5A", + "00000220 83 46 20 31 54 87 89 BF 12 91 05 01 D9 62 35 61", + "00000230 E2 0E 04 4A B3 03 09 A1 18 0B 2D 45 69 BD AD 93", + "00000240 A3 0D 7D 26 3E B4 EE 42 28 F6 40 37 C9 DE AD B6", + "00000250 1F 73 4A 54 A1 E2 4F C3 7F 4C D9 1E 70 B3 E9 4F", + "00000260 94 BC 7C 39 2B B7 44 12 BC 5B 94 38 49 EE F5 16", + "00000270 8E 84 91 3A 5F 21 A0 17 4B 48 28 EA 5B 89 71 B6", + "00000280 67 2F 3B E5 42 C2 76 45 8E 25 94 85 21 2D 1F 19", + "00000290 24 83 DF C7 76 E6 85 7D DB A6 69 A7 62 66 5D 9E", + "000002A0 DB F9 D4 1E B5 F9 C9 2D B3 66 46 20 87 7D 75 0D", + "000002B0 E4 8B 88 CD 43 32 D1 6F 5A 20 CD DA 5E C7 39 8A", + "000002C0 EC 40 6D B6 38 79 B8 DB D2 2D 07 A6 C0 8C E7 00", + "000002D0 45 D0 B1 65 7C 74 8C 4C E9 7C 06 59 6D 1A 8B CF", + "000002E0 F8 D9 AB A7 7C EA 77 C2 B3 FF 35 17 78 67 EF 2E", + "000002F0 8A CD CC EF 67 BF E8 1B A8 7F 24 6D 02 CB 1B D9", + "00000300 E9 D4 74 6A 03 D3 1E CE 97 14 C2 DE 63 1F DC 62", + "00000310 88 96 85 8D DE DF 27 A4 A5 8D 79 0B 91 7E C5 A6", + "00000320 10 A2 D0 36 65 8E 77 63 EF 92 1F 47 D2 26 0D 4D", + "00000330 E1 92 1C 9D 81 B3 FA D1 75 DE E7 C9 C1 DC B7 72", + "00000340 08 CA 90 EC F0 41 56 21 DA C9 8F 75 20 FD 98 29", + "00000350 5F C9 D4 34 68 A1 34 32 D8 47 BC 04 C0 DC 6C 8A", + "00000360 7A 87 9A 5B B2 9C 36 87 6A 11 FB FE 55 96 80 0C", + "00000370 5F 29 19 07 83 8C 9E 95 9A 58 F1 F0 49 36 70 F0", + "00000380 8D 49 3F D6 C5 CE DE" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Base64 URL: Large Length (1200 bytes) + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.String( + "2hhLDu-G0Ca3khg9lR4cjTF3gwDffhdQ8dC8lzXzV9l6I_ahWqUwNc0Deqobc3pjqVIpujXVFLQc5r64k4oj0s8PhsbFeNsnqJ43SXRG3e_iYTNEIflgVyLT" + + "c6Z5SQq8KvKapY0R-bgg8GgoUcIKIxcgmOds1MTN-deCcrNUkFgz5pelkIGfVxZEFZVWvsG6osL7pMMBzA_YSRhctJyX1Sx3f6921S1ySlWjpIbLH_nAthBw" + + "mCuZ38nPu7vU7yWDKHIu3KhfQOqV03Ld1OSgkjCSZEZGfkImWP4ZaA1OpU40Jnf2SZf8By_VdOx0_bMS1rMw6rOlNQiDzmG438fSKureqDyFLvIi8VpOyWjO" + + "7GBZKTEzGleOxPqOcVzL2l0zGa9wjDGTIP837XVTGF1Dt2iNjG8EjuXtdiZO6xCgTRiPHnF5iL2rl9rX-C_7raMVDKpSwa8H3Pdbya3OsaDsUlqr1P7Z-VC7" + + "Hkxu-cGwrgNVcpClURWHzoz86wt-XvrNJ2WPaDBPIkfRFyvkHPkkhGwxU0nrc0dQBTKWVIRVNpvh__S1WwIYU0l_SuRzNopu0h7729wH9zlUqI_2DlodjIJx" + + "ObBihPOFWWMo7DX5zWK-dtEGTAF3hEPzLQ2FM3ooq03XZuLfgumXfOSnyx3an1D0fgcefn83tecnp5yZtwEigAQsUGuT702xPN8fzpxQcSOJ7TtKYGiQkFqD" + + "RiAxVIeJvxKRBQHZYjVh4g4ESrMDCaEYCy1Fab2tk6MNfSY-tO5CKPZAN8nerbYfc0pUoeJPw39M2R5ws-lPlLx8OSu3RBK8W5Q4Se71Fo6EkTpfIaAXS0go" + + "6luJcbZnLzvlQsJ2RY4llIUhLR8ZJIPfx3bmhX3bpmmnYmZdntv51B61-ckts2ZGIId9dQ3ki4jNQzLRb1ogzdpexzmK7EBttjh5uNvSLQemwIznAEXQsWV8" + + "dIxM6XwGWW0ai8_42aunfOp3wrP_NRd4Z-8uis3M72e_6BuofyRtAssb2enUdGoD0x7OlxTC3mMf3GKIloWN3t8npKWNeQuRfsWmEKLQNmWOd2Pvkh9H0iYN" + + "TeGSHJ2Bs_rRdd7nycHct3IIypDs8EFWIdrJj3Ug_ZgpX8nUNGihNDLYR7wEwNxsinqHmluynDaHahH7_lWWgAxfKRkHg4yelZpY8fBJNnDwjUk_1sXO3g==") + }; + + string[] expectedText = + { + @"""2hhLDu-G0Ca3khg9lR4cjTF3gwDffhdQ8dC8lzXzV9l6I_ahWqUwNc0Deqobc3pjqVIpujXVFLQc5r64k4oj0s8PhsbFeNsnqJ4", + @"3SXRG3e_iYTNEIflgVyLTc6Z5SQq8KvKapY0R-bgg8GgoUcIKIxcgmOds1MTN-deCcrNUkFgz5pelkIGfVxZEFZVWvsG6osL7pMM", + @"BzA_YSRhctJyX1Sx3f6921S1ySlWjpIbLH_nAthBwmCuZ38nPu7vU7yWDKHIu3KhfQOqV03Ld1OSgkjCSZEZGfkImWP4ZaA1OpU4", + @"0Jnf2SZf8By_VdOx0_bMS1rMw6rOlNQiDzmG438fSKureqDyFLvIi8VpOyWjO7GBZKTEzGleOxPqOcVzL2l0zGa9wjDGTIP837XV", + @"TGF1Dt2iNjG8EjuXtdiZO6xCgTRiPHnF5iL2rl9rX-C_7raMVDKpSwa8H3Pdbya3OsaDsUlqr1P7Z-VC7Hkxu-cGwrgNVcpClURW", + @"Hzoz86wt-XvrNJ2WPaDBPIkfRFyvkHPkkhGwxU0nrc0dQBTKWVIRVNpvh__S1WwIYU0l_SuRzNopu0h7729wH9zlUqI_2DlodjIJ", + @"xObBihPOFWWMo7DX5zWK-dtEGTAF3hEPzLQ2FM3ooq03XZuLfgumXfOSnyx3an1D0fgcefn83tecnp5yZtwEigAQsUGuT702xPN8", + @"fzpxQcSOJ7TtKYGiQkFqDRiAxVIeJvxKRBQHZYjVh4g4ESrMDCaEYCy1Fab2tk6MNfSY-tO5CKPZAN8nerbYfc0pUoeJPw39M2R5", + @"ws-lPlLx8OSu3RBK8W5Q4Se71Fo6EkTpfIaAXS0go6luJcbZnLzvlQsJ2RY4llIUhLR8ZJIPfx3bmhX3bpmmnYmZdntv51B61-ck", + @"ts2ZGIId9dQ3ki4jNQzLRb1ogzdpexzmK7EBttjh5uNvSLQemwIznAEXQsWV8dIxM6XwGWW0ai8_42aunfOp3wrP_NRd4Z-8uis3", + @"M72e_6BuofyRtAssb2enUdGoD0x7OlxTC3mMf3GKIloWN3t8npKWNeQuRfsWmEKLQNmWOd2Pvkh9H0iYNTeGSHJ2Bs_rRdd7nycH", + @"ct3IIypDs8EFWIdrJj3Ug_ZgpX8nUNGihNDLYR7wEwNxsinqHmluynDaHahH7_lWWgAxfKRkHg4yelZpY8fBJNnDwjUk_1sXO3g=", + @"=""" + }; + + string[] expectedBinary1 = + { + "00000000 80 7F B0 04 32 34 9A 49 AC B7 8E B0 61 78 B6 46", + "00000010 9F 73 6C 29 6D AC A6 1A 67 E7 3B D1 6C 46 93 A3", + "00000020 38 F2 10 C7 D6 63 F5 D6 1C DB 96 FC 86 D1 D7 78", + "00000030 F5 EE 1C C3 88 E5 F8 5B 3C 9E C1 D5 71 6B 12 5E", + "00000040 57 63 AD 46 66 74 5C 93 DB 68 6B DA 5B 0D 9B E3", + "00000050 A0 E8 B9 D8 58 76 CE DD 71 25 6D 36 C5 4A 8F B3", + "00000060 F2 37 9D A5 3A 8B 49 33 FB 6C CD 33 A9 63 9B B6", + "00000070 36 8D C6 71 4B FB 32 0C CF C2 A4 2D F1 F9 8C 3B", + "00000080 9E DF D5 71 72 99 C4 8F CF ED 27 79 1E 6B 52 9D", + "00000090 2D 72 79 38 96 3B AB 6B E3 59 5F 83 97 D9 EB E4", + "000000A0 D1 6C C5 6B 8B 46 AD F5 6A 9F 1F 6D EF 39 F3 06", + "000000B0 6F 36 85 FA E0 37 3B 95 A2 C7 74 65 1E 1B 9B E2", + "000000C0 67 66 5B 4E 16 9B C6 F2 53 F6 55 0D 4F 8A 99 C8", + "000000D0 AF 3B 48 47 0B EF ED 61 5D 3B C3 B9 A1 F5 9B BD", + "000000E0 7A CB 5F 89 4B 64 B2 3E 5B A2 CD D1 67 DC 0A 9B", + "000000F0 31 C9 B1 E7 F4 BC 56 0F A7 DA A2 F6 68 5E 27 DB", + "00000100 57 28 4D 1B 0E C6 9E F0 2A 0D A6 74 9B 65 53 AD", + "00000110 19 27 CC 7F AD E4 27 1E F6 15 37 A7 31 79 F3 6E", + "00000120 93 3F D9 CE 68 9A A8 6F 1F 69 33 9C 79 BA AC CB", + "00000130 CB 71 62 DE C8 B4 27 D3 38 2B FC 99 BF AA 9F B7", + "00000140 A3 50 BB A4 16 F5 47 76 F9 89 87 C6 9F 63 AB 9E", + "00000150 29 63 C3 F4 C7 70 EE AE 26 1E A9 49 28 6E 76 C3", + "00000160 5A A9 47 63 8C 48 97 A5 9D EA 23 AE A8 AE 63 E9", + "00000170 E4 B4 F6 69 C3 0F CF 54 69 1A 8A 74 1B 6B 69 A6", + "00000180 4C CE CE C9 B1 AD E1 F7 26 0F 37 AD C4 25 7C 7A", + "00000190 0F E3 90 33 28 59 9C 0F CF 9E F3 30 71 5E 65 C7", + "000001A0 E5 31 E8 4D DB B2 0E 6F C8 35 BE DE 1A 1F EF F2", + "000001B0 B3 D3 3A 86 0F D9 55 E9 15 A9 7F EB 71 B6 3B BD", + "000001C0 85 B5 CB 9D 4A D9 15 1A 26 0A A1 C9 B5 59 6A CC", + "000001D0 DB D7 48 E8 7A 8D 3E DE F1 55 98 5B 3E 86 91 A3", + "000001E0 42 EA F2 6A 4D 4A AD 4E B8 1D FD FD 4E 63 D7 7B", + "000001F0 32 5B 85 B1 BF D3 BA 54 EF 7C C3 EB 30 F4 ED 26", + "00000200 CB DD 91 39 3D BB 1A 4F 7E 65 44 F6 9B AC 4E 2A", + "00000210 F1 4F B1 30 8D 86 3E 8D D7 6B F3 7D 23 62 6B FA", + "00000220 EB B2 45 A6 17 8F D4 A0 71 86 2E 42 F5 CC A8 CC", + "00000230 D8 9C BD DF 71 D8 0C AB AD 33 CD E7 7A 1B 6B 7E", + "00000240 4E DD 79 FC 2C EC 8E 11 61 E6 F3 B8 6C 76 E3 66", + "00000250 F4 F2 D8 0D AF E5 B5 F4 7B 31 7D 0E 46 E7 D5 63", + "00000260 9D 7A 83 C9 F0 50 27 CE AC 87 E3 A3 E3 E9 53 79", + "00000270 A3 D2 97 D9 63 3A BA 36 C6 89 D2 74 10 6F 4D 96", + "00000280 95 76 FC 52 2A 8C 22 B5 59 B5 15 4D 3B D3 8A 53", + "00000290 79 93 38 0C 17 B3 C3 7C CC 18 16 CB E8 6B 5B D3", + "000002A0 69 9E 66 5B F4 67 6D B8 84 6A 83 4E 9C BB 2C 17", + "000002B0 67 CD 63 18 BC FA 2E 2B A1 F7 59 AE 29 93 D6 EE", + "000002C0 F3 16 1B CA 66 E2 71 CF 69 7D 26 15 2E 71 D7 5A", + "000002D0 94 36 2D DF 62 C6 B7 AD B8 A6 C2 CD C9 70 10 3B", + "000002E0 85 9D DF 36 76 5D 39 16 6B DD 4C BD 9D 1D 9D 2B", + "000002F0 65 D2 2C 8D CD 4E 56 D1 4C 29 4E AB 4C 42 CD F8", + "00000300 99 B8 8D C6 CE C4 F0 76 DB 9D 6D 6B C9 6E BA BD", + "00000310 16 13 DA 62 AD F1 9A 3E 97 69 8F C9 24 39 47 8E", + "00000320 CE D6 69 9A DA 19 D5 33 A5 E2 D8 FB AC 27 C3 CB", + "00000330 78 7D 7B 79 2B 0A E9 74 35 BA 56 77 DA A7 CC 68", + "00000340 B9 7D 4F EA DD C1 22 36 3A BF 5A 71 E4 24 BE 69", + "00000350 C3 DE 8F D7 2B 2C 9C C6 7D 69 B2 70 DD 6D 7E C2", + "00000360 67 77 39 F4 EB 94 92 69 DA 16 AE 9E 9E CF 9A 37", + "00000370 59 F9 6B 13 D6 DF E6 BC 94 1E 9C CF C5 B2 B2 BB", + "00000380 4A 3E BE 89 30 FC ED C9 C6 53 87 B3 76 D3 3C 3B", + "00000390 2E 93 EC F7 D5 39 A3 E3 DC F0 E5 D5 59 8E D6 A5", + "000003A0 E6 F9 B5 5D 5C 32 A3 CE F6 F5 49 96 41 ED 6B 74", + "000003B0 0E 09 4B 67 9D D4 F2 71 8A 54 CA 84 F3 AF 5C 4A", + "000003C0 26 DF DC F9 31 72 4C 9F 25 93 79 38 71 8E 2B 1A", + "000003D0 AF 49 B2 5C A9 9E 55 CF 5F ED 19 8E C5 B9 AB CE", + "000003E0 63 1A ED 24 32 B3 D2 DB BD 78 77 E2 E7 69 77 1C", + "000003F0 D9 66 D7 F3 6E 62 18 19 46 23 6F 5F F6 F5 7A 0E", + "00000400 E2 CD 4B E9 1A 79 A6 E5 CB 6C 2D 3C 8B 33 0B 95", + "00000410 4E 37 F1 AE AE AE BF B1 39 F6 39 3B F7 7A" + }; + + string[] expectedBinary2 = + { + "00000000 80 74 2C 01 02 DA 18 4B 0E EF 86 D0 26 B7 92 18", + "00000010 3D 95 1E 1C 8D 31 77 83 00 DF 7E 17 50 F1 D0 BC", + "00000020 97 35 F3 57 D9 7A 23 F6 A1 5A A5 30 35 CD 03 7A", + "00000030 AA 1B 73 7A 63 A9 52 29 BA 35 D5 14 B4 1C E6 BE", + "00000040 B8 93 8A 23 D2 CF 0F 86 C6 C5 78 DB 27 A8 9E 37", + "00000050 49 74 46 DD EF E2 61 33 44 21 F9 60 57 22 D3 73", + "00000060 A6 79 49 0A BC 2A F2 9A A5 8D 11 F9 B8 20 F0 68", + "00000070 28 51 C2 0A 23 17 20 98 E7 6C D4 C4 CD F9 D7 82", + "00000080 72 B3 54 90 58 33 E6 97 A5 90 81 9F 57 16 44 15", + "00000090 95 56 BE C1 BA A2 C2 FB A4 C3 01 CC 0F D8 49 18", + "000000A0 5C B4 9C 97 D5 2C 77 7F AF 76 D5 2D 72 4A 55 A3", + "000000B0 A4 86 CB 1F F9 C0 B6 10 70 98 2B 99 DF C9 CF BB", + "000000C0 BB D4 EF 25 83 28 72 2E DC A8 5F 40 EA 95 D3 72", + "000000D0 DD D4 E4 A0 92 30 92 64 46 46 7E 42 26 58 FE 19", + "000000E0 68 0D 4E A5 4E 34 26 77 F6 49 97 FC 07 2F D5 74", + "000000F0 EC 74 FD B3 12 D6 B3 30 EA B3 A5 35 08 83 CE 61", + "00000100 B8 DF C7 D2 2A EA DE A8 3C 85 2E F2 22 F1 5A 4E", + "00000110 C9 68 CE EC 60 59 29 31 33 1A 57 8E C4 FA 8E 71", + "00000120 5C CB DA 5D 33 19 AF 70 8C 31 93 20 FF 37 ED 75", + "00000130 53 18 5D 43 B7 68 8D 8C 6F 04 8E E5 ED 76 26 4E", + "00000140 EB 10 A0 4D 18 8F 1E 71 79 88 BD AB 97 DA D7 F8", + "00000150 2F FB AD A3 15 0C AA 52 C1 AF 07 DC F7 5B C9 AD", + "00000160 CE B1 A0 EC 52 5A AB D4 FE D9 F9 50 BB 1E 4C 6E", + "00000170 F9 C1 B0 AE 03 55 72 90 A5 51 15 87 CE 8C FC EB", + "00000180 0B 7E 5E FA CD 27 65 8F 68 30 4F 22 47 D1 17 2B", + "00000190 E4 1C F9 24 84 6C 31 53 49 EB 73 47 50 05 32 96", + "000001A0 54 84 55 36 9B E1 FF F4 B5 5B 02 18 53 49 7F 4A", + "000001B0 E4 73 36 8A 6E D2 1E FB DB DC 07 F7 39 54 A8 8F", + "000001C0 F6 0E 5A 1D 8C 82 71 39 B0 62 84 F3 85 59 63 28", + "000001D0 EC 35 F9 CD 62 BE 76 D1 06 4C 01 77 84 43 F3 2D", + "000001E0 0D 85 33 7A 28 AB 4D D7 66 E2 DF 82 E9 97 7C E4", + "000001F0 A7 CB 1D DA 9F 50 F4 7E 07 1E 7E 7F 37 B5 E7 27", + "00000200 A7 9C 99 B7 01 22 80 04 2C 50 6B 93 EF 4D B1 3C", + "00000210 DF 1F CE 9C 50 71 23 89 ED 3B 4A 60 68 90 90 5A", + "00000220 83 46 20 31 54 87 89 BF 12 91 05 01 D9 62 35 61", + "00000230 E2 0E 04 4A B3 03 09 A1 18 0B 2D 45 69 BD AD 93", + "00000240 A3 0D 7D 26 3E B4 EE 42 28 F6 40 37 C9 DE AD B6", + "00000250 1F 73 4A 54 A1 E2 4F C3 7F 4C D9 1E 70 B3 E9 4F", + "00000260 94 BC 7C 39 2B B7 44 12 BC 5B 94 38 49 EE F5 16", + "00000270 8E 84 91 3A 5F 21 A0 17 4B 48 28 EA 5B 89 71 B6", + "00000280 67 2F 3B E5 42 C2 76 45 8E 25 94 85 21 2D 1F 19", + "00000290 24 83 DF C7 76 E6 85 7D DB A6 69 A7 62 66 5D 9E", + "000002A0 DB F9 D4 1E B5 F9 C9 2D B3 66 46 20 87 7D 75 0D", + "000002B0 E4 8B 88 CD 43 32 D1 6F 5A 20 CD DA 5E C7 39 8A", + "000002C0 EC 40 6D B6 38 79 B8 DB D2 2D 07 A6 C0 8C E7 00", + "000002D0 45 D0 B1 65 7C 74 8C 4C E9 7C 06 59 6D 1A 8B CF", + "000002E0 F8 D9 AB A7 7C EA 77 C2 B3 FF 35 17 78 67 EF 2E", + "000002F0 8A CD CC EF 67 BF E8 1B A8 7F 24 6D 02 CB 1B D9", + "00000300 E9 D4 74 6A 03 D3 1E CE 97 14 C2 DE 63 1F DC 62", + "00000310 88 96 85 8D DE DF 27 A4 A5 8D 79 0B 91 7E C5 A6", + "00000320 10 A2 D0 36 65 8E 77 63 EF 92 1F 47 D2 26 0D 4D", + "00000330 E1 92 1C 9D 81 B3 FA D1 75 DE E7 C9 C1 DC B7 72", + "00000340 08 CA 90 EC F0 41 56 21 DA C9 8F 75 20 FD 98 29", + "00000350 5F C9 D4 34 68 A1 34 32 D8 47 BC 04 C0 DC 6C 8A", + "00000360 7A 87 9A 5B B2 9C 36 87 6A 11 FB FE 55 96 80 0C", + "00000370 5F 29 19 07 83 8C 9E 95 9A 58 F1 F0 49 36 70 F0", + "00000380 8D 49 3F D6 C5 CE DE" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Base64/Base64 URL: No Padding + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ArrayStart(), + JsonToken.String("bcaW+OlS0pKrBj83ZNPAnuMa+7CA7q0kUWaC1wg="), + JsonToken.String("bcaW+OlS0pKrBj83ZNPAnuMa+7CA7q0kUWaC1wg"), + JsonToken.String("bcaW-OlS0pKrBj83ZNPAnuMa-7CA7q0kUWaC1wg"), + JsonToken.String("vFniJGPEGDCL7ijlj68HafJ9uzmmR6JyXvs6Jl5si+W2LA=="), + JsonToken.String("vFniJGPEGDCL7ijlj68HafJ9uzmmR6JyXvs6Jl5si+W2LA"), + JsonToken.String("vFniJGPEGDCL7ijlj68HafJ9uzmmR6JyXvs6Jl5si-W2LA"), + JsonToken.String("d/8fH/1VHFcZLeMe5mCf4pUR1WMoy9c3plC8ZBvruB7YPVk="), + JsonToken.String("d/8fH/1VHFcZLeMe5mCf4pUR1WMoy9c3plC8ZBvruB7YPVk"), + JsonToken.String("d_8fH_1VHFcZLeMe5mCf4pUR1WMoy9c3plC8ZBvruB7YPVk"), + JsonToken.String("LtV/FCBuonw12i57cKNa+p0oV40zI4Anw/Qhbg6K0JFVDQ=="), + JsonToken.String("LtV/FCBuonw12i57cKNa+p0oV40zI4Anw/Qhbg6K0JFVDQ"), + JsonToken.String("LtV_FCBuonw12i57cKNa-p0oV40zI4Anw_Qhbg6K0JFVDQ"), + JsonToken.String("tl80G/hSrQX1khaCMajsybfL/hLKz2dnujkXpDeuK1qhKGYZionK3C/1tg=="), + JsonToken.String("tl80G/hSrQX1khaCMajsybfL/hLKz2dnujkXpDeuK1qhKGYZionK3C/1tg"), + JsonToken.String("tl80G_hSrQX1khaCMajsybfL_hLKz2dnujkXpDeuK1qhKGYZionK3C_1tg"), + JsonToken.ArrayEnd(), + }; + + string[] expectedText = + { + @"[""bcaW+OlS0pKrBj83ZNPAnuMa+7CA7q0kUWaC1wg="",""bcaW+OlS0pKrBj83ZNPAnuMa+7CA7q0kUWaC1wg"",""bcaW-OlS0pKrB", + @"j83ZNPAnuMa-7CA7q0kUWaC1wg"",""vFniJGPEGDCL7ijlj68HafJ9uzmmR6JyXvs6Jl5si+W2LA=="",""vFniJGPEGDCL7ijlj68H", + @"afJ9uzmmR6JyXvs6Jl5si+W2LA"",""vFniJGPEGDCL7ijlj68HafJ9uzmmR6JyXvs6Jl5si-W2LA"",""d/8fH/1VHFcZLeMe5mCf4p", + @"UR1WMoy9c3plC8ZBvruB7YPVk="",""d/8fH/1VHFcZLeMe5mCf4pUR1WMoy9c3plC8ZBvruB7YPVk"",""d_8fH_1VHFcZLeMe5mCf4", + @"pUR1WMoy9c3plC8ZBvruB7YPVk"",""LtV/FCBuonw12i57cKNa+p0oV40zI4Anw/Qhbg6K0JFVDQ=="",""LtV/FCBuonw12i57cKNa", + @"+p0oV40zI4Anw/Qhbg6K0JFVDQ"",""LtV_FCBuonw12i57cKNa-p0oV40zI4Anw_Qhbg6K0JFVDQ"",""tl80G/hSrQX1khaCMajsyb", + @"fL/hLKz2dnujkXpDeuK1qhKGYZionK3C/1tg=="",""tl80G/hSrQX1khaCMajsybfL/hLKz2dnujkXpDeuK1qhKGYZionK3C/1tg""", + @",""tl80G_hSrQX1khaCMajsybfL_hLKz2dnujkXpDeuK1qhKGYZionK3C_1tg""]" + }; + + string[] expectedBinary1 = + { + "00000000 80 E3 DB 02 A8 62 63 61 57 2B 4F 6C 53 30 70 4B", + "00000010 72 42 6A 38 33 5A 4E 50 41 6E 75 4D 61 2B 37 43", + "00000020 41 37 71 30 6B 55 57 61 43 31 77 67 3D A7 62 63", + "00000030 61 57 2B 4F 6C 53 30 70 4B 72 42 6A 38 33 5A 4E", + "00000040 50 41 6E 75 4D 61 2B 37 43 41 37 71 30 6B 55 57", + "00000050 61 43 31 77 67 A7 62 63 61 57 2D 4F 6C 53 30 70", + "00000060 4B 72 42 6A 38 33 5A 4E 50 41 6E 75 4D 61 2D 37", + "00000070 43 41 37 71 30 6B 55 57 61 43 31 77 67 B0 76 46", + "00000080 6E 69 4A 47 50 45 47 44 43 4C 37 69 6A 6C 6A 36", + "00000090 38 48 61 66 4A 39 75 7A 6D 6D 52 36 4A 79 58 76", + "000000A0 73 36 4A 6C 35 73 69 2B 57 32 4C 41 3D 3D AE 76", + "000000B0 46 6E 69 4A 47 50 45 47 44 43 4C 37 69 6A 6C 6A", + "000000C0 36 38 48 61 66 4A 39 75 7A 6D 6D 52 36 4A 79 58", + "000000D0 76 73 36 4A 6C 35 73 69 2B 57 32 4C 41 AE 76 46", + "000000E0 6E 69 4A 47 50 45 47 44 43 4C 37 69 6A 6C 6A 36", + "000000F0 38 48 61 66 4A 39 75 7A 6D 6D 52 36 4A 79 58 76", + "00000100 73 36 4A 6C 35 73 69 2D 57 32 4C 41 B0 64 2F 38", + "00000110 66 48 2F 31 56 48 46 63 5A 4C 65 4D 65 35 6D 43", + "00000120 66 34 70 55 52 31 57 4D 6F 79 39 63 33 70 6C 43", + "00000130 38 5A 42 76 72 75 42 37 59 50 56 6B 3D AF 64 2F", + "00000140 38 66 48 2F 31 56 48 46 63 5A 4C 65 4D 65 35 6D", + "00000150 43 66 34 70 55 52 31 57 4D 6F 79 39 63 33 70 6C", + "00000160 43 38 5A 42 76 72 75 42 37 59 50 56 6B AF 64 5F", + "00000170 38 66 48 5F 31 56 48 46 63 5A 4C 65 4D 65 35 6D", + "00000180 43 66 34 70 55 52 31 57 4D 6F 79 39 63 33 70 6C", + "00000190 43 38 5A 42 76 72 75 42 37 59 50 56 6B B0 4C 74", + "000001A0 56 2F 46 43 42 75 6F 6E 77 31 32 69 35 37 63 4B", + "000001B0 4E 61 2B 70 30 6F 56 34 30 7A 49 34 41 6E 77 2F", + "000001C0 51 68 62 67 36 4B 30 4A 46 56 44 51 3D 3D AE 4C", + "000001D0 74 56 2F 46 43 42 75 6F 6E 77 31 32 69 35 37 63", + "000001E0 4B 4E 61 2B 70 30 6F 56 34 30 7A 49 34 41 6E 77", + "000001F0 2F 51 68 62 67 36 4B 30 4A 46 56 44 51 AE 4C 74", + "00000200 56 5F 46 43 42 75 6F 6E 77 31 32 69 35 37 63 4B", + "00000210 4E 61 2D 70 30 6F 56 34 30 7A 49 34 41 6E 77 5F", + "00000220 51 68 62 67 36 4B 30 4A 46 56 44 51 BC 74 6C 38", + "00000230 30 47 2F 68 53 72 51 58 31 6B 68 61 43 4D 61 6A", + "00000240 73 79 62 66 4C 2F 68 4C 4B 7A 32 64 6E 75 6A 6B", + "00000250 58 70 44 65 75 4B 31 71 68 4B 47 59 5A 69 6F 6E", + "00000260 4B 33 43 2F 31 74 67 3D 3D BA 74 6C 38 30 47 2F", + "00000270 68 53 72 51 58 31 6B 68 61 43 4D 61 6A 73 79 62", + "00000280 66 4C 2F 68 4C 4B 7A 32 64 6E 75 6A 6B 58 70 44", + "00000290 65 75 4B 31 71 68 4B 47 59 5A 69 6F 6E 4B 33 43", + "000002A0 2F 31 74 67 BA 74 6C 38 30 47 5F 68 53 72 51 58", + "000002B0 31 6B 68 61 43 4D 61 6A 73 79 62 66 4C 5F 68 4C", + "000002C0 4B 7A 32 64 6E 75 6A 6B 58 70 44 65 75 4B 31 71", + "000002D0 68 4B 47 59 5A 69 6F 6E 4B 33 43 5F 31 74 67" + }; + + string[] expectedBinary2 = + { + "00000000 80 E3 4A 02 71 0A 01 6D C6 96 F8 E9 52 D2 92 AB", + "00000010 06 3F 37 64 D3 C0 9E E3 1A FB B0 80 EE AD 24 51", + "00000020 66 82 D7 08 A7 62 63 61 57 2B 4F 6C 53 30 70 4B", + "00000030 72 42 6A 38 33 5A 4E 50 41 6E 75 4D 61 2B 37 43", + "00000040 41 37 71 30 6B 55 57 61 43 31 77 67 A7 62 63 61", + "00000050 57 2D 4F 6C 53 30 70 4B 72 42 6A 38 33 5A 4E 50", + "00000060 41 6E 75 4D 61 2D 37 43 41 37 71 30 6B 55 57 61", + "00000070 43 31 77 67 71 0C 02 BC 59 E2 24 63 C4 18 30 8B", + "00000080 EE 28 E5 8F AF 07 69 F2 7D BB 39 A6 47 A2 72 5E", + "00000090 FB 3A 26 5E 6C 8B E5 B6 2C 71 0C FD BC 59 E2 24", + "000000A0 63 C4 18 30 8B EE 28 E5 8F AF 07 69 F2 7D BB 39", + "000000B0 A6 47 A2 72 5E FB 3A 26 5E 6C 8B E5 B6 2C 73 0C", + "000000C0 FD BC 59 E2 24 63 C4 18 30 8B EE 28 E5 8F AF 07", + "000000D0 69 F2 7D BB 39 A6 47 A2 72 5E FB 3A 26 5E 6C 8B", + "000000E0 E5 B6 2C 71 0C 01 77 FF 1F 1F FD 55 1C 57 19 2D", + "000000F0 E3 1E E6 60 9F E2 95 11 D5 63 28 CB D7 37 A6 50", + "00000100 BC 64 1B EB B8 1E D8 3D 59 71 0C FE 77 FF 1F 1F", + "00000110 FD 55 1C 57 19 2D E3 1E E6 60 9F E2 95 11 D5 63", + "00000120 28 CB D7 37 A6 50 BC 64 1B EB B8 1E D8 3D 59 73", + "00000130 0C FE 77 FF 1F 1F FD 55 1C 57 19 2D E3 1E E6 60", + "00000140 9F E2 95 11 D5 63 28 CB D7 37 A6 50 BC 64 1B EB", + "00000150 B8 1E D8 3D 59 71 0C 02 2E D5 7F 14 20 6E A2 7C", + "00000160 35 DA 2E 7B 70 A3 5A FA 9D 28 57 8D 33 23 80 27", + "00000170 C3 F4 21 6E 0E 8A D0 91 55 0D 71 0C FD 2E D5 7F", + "00000180 14 20 6E A2 7C 35 DA 2E 7B 70 A3 5A FA 9D 28 57", + "00000190 8D 33 23 80 27 C3 F4 21 6E 0E 8A D0 91 55 0D 73", + "000001A0 0C FD 2E D5 7F 14 20 6E A2 7C 35 DA 2E 7B 70 A3", + "000001B0 5A FA 9D 28 57 8D 33 23 80 27 C3 F4 21 6E 0E 8A", + "000001C0 D0 91 55 0D 71 0F 02 B6 5F 34 1B F8 52 AD 05 F5", + "000001D0 92 16 82 31 A8 EC C9 B7 CB FE 12 CA CF 67 67 BA", + "000001E0 39 17 A4 37 AE 2B 5A A1 28 66 19 8A 89 CA DC 2F", + "000001F0 F5 B6 71 0F FD B6 5F 34 1B F8 52 AD 05 F5 92 16", + "00000200 82 31 A8 EC C9 B7 CB FE 12 CA CF 67 67 BA 39 17", + "00000210 A4 37 AE 2B 5A A1 28 66 19 8A 89 CA DC 2F F5 B6", + "00000220 73 0F FD B6 5F 34 1B F8 52 AD 05 F5 92 16 82 31", + "00000230 A8 EC C9 B7 CB FE 12 CA CF 67 67 BA 39 17 A4 37", + "00000240 AE 2B 5A A1 28 66 19 8A 89 CA DC 2F F5 B6" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Base64/Base64 URL: Regular Strings + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ArrayStart(), + JsonToken.String("HNF_DietTracker_Aggregate2014-08-10_-5561547689238327167"), + JsonToken.String("HNF_DietTracker_Aggregate2014-09-05_3143222292013052466"), + JsonToken.String("HNF_DietTracker_Aggregate2014-09-05_3143222292013052464"), + JsonToken.String("--QHPKZTRVMNSFYSNXFHIAIMIZEIBIWAQJLMPVDZVNDXCXYTFQTKLUBBHEYQGQSIYL__"), + JsonToken.String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"), + JsonToken.ArrayEnd(), + }; + + string[] expectedText = + { + @"[""HNF_DietTracker_Aggregate2014-08-10_-5561547689238327167"",""HNF_DietTracker_Aggregate2014-09-05_314", + @"3222292013052466"",""HNF_DietTracker_Aggregate2014-09-05_3143222292013052464"",""--QHPKZTRVMNSFYSNXFHIAI", + @"MIZEIBIWAQJLMPVDZVNDXCXYTFQTKLUBBHEYQGQSIYL__"",""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + @"0123456789-_""]" + }; + + string[] expectedBinary1 = + { + "00000000 80 E3 21 01 B8 48 4E 46 5F 44 69 65 74 54 72 61", + "00000010 63 6B 65 72 5F 41 67 67 72 65 67 61 74 65 32 30", + "00000020 31 34 2D 30 38 2D 31 30 5F 2D 35 35 36 31 35 34", + "00000030 37 36 38 39 32 33 38 33 32 37 31 36 37 B7 48 4E", + "00000040 46 5F 44 69 65 74 54 72 61 63 6B 65 72 5F 41 67", + "00000050 67 72 65 67 61 74 65 32 30 31 34 2D 30 39 2D 30", + "00000060 35 5F 33 31 34 33 32 32 32 32 39 32 30 31 33 30", + "00000070 35 32 34 36 36 B7 48 4E 46 5F 44 69 65 74 54 72", + "00000080 61 63 6B 65 72 5F 41 67 67 72 65 67 61 74 65 32", + "00000090 30 31 34 2D 30 39 2D 30 35 5F 33 31 34 33 32 32", + "000000A0 32 32 39 32 30 31 33 30 35 32 34 36 34 7D 44 2D", + "000000B0 00 40 6E A3 D7 9E 65 0A 86 66 C6 9A E1 9A 6D 1C", + "000000C0 C5 81 5C 8B 71 15 A7 52 64 F7 81 63 7A B5 69 78", + "000000D0 AD D6 CA 9E 19 79 7A 1F 5A 55 1B C6 92 1A 69 72", + "000000E0 EC 27 CB C0 40 61 62 63 64 65 66 67 68 69 6A 6B", + "000000F0 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 41", + "00000100 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51", + "00000110 52 53 54 55 56 57 58 59 5A 30 31 32 33 34 35 36", + "00000120 37 38 39 2D 5F" + }; + + string[] expectedBinary2 = + { + "00000000 80 E2 FA 73 0E 00 1C D1 7F 0E 27 AD 4E B6 9C 91", + "00000010 EA FF 02 08 2B 7A 06 AD 7B 6D 35 E3 ED 3C FB 5D", + "00000020 3F FB 9E 7A D7 9E 3B EB CF 76 DF CD F6 EF 5E BB", + "00000030 B7 48 4E 46 5F 44 69 65 74 54 72 61 63 6B 65 72", + "00000040 5F 41 67 67 72 65 67 61 74 65 32 30 31 34 2D 30", + "00000050 39 2D 30 35 5F 33 31 34 33 32 32 32 32 39 32 30", + "00000060 31 33 30 35 32 34 36 36 73 0E FE 1C D1 7F 0E 27", + "00000070 AD 4E B6 9C 91 EA FF 02 08 2B 7A 06 AD 7B 6D 35", + "00000080 E3 ED 3D FB 4E 7F DF 5E 37 DB 6D B6 F7 6D 35 DF", + "00000090 4E 76 E3 AE 7D 44 2D 00 40 6E A3 D7 9E 65 0A 86", + "000000A0 66 C6 9A E1 9A 6D 1C C5 81 5C 8B 71 15 A7 52 64", + "000000B0 F7 81 63 7A B5 69 78 AD D6 CA 9E 19 79 7A 1F 5A", + "000000C0 55 1B C6 92 1A 69 72 EC 27 CB 73 10 00 69 B7 1D", + "000000D0 79 F8 21 8A 39 25 9A 7A 29 AA BB 2D BA FC 31 CB", + "000000E0 30 01 08 31 05 18 72 09 28 B3 0D 38 F4 11 49 35", + "000000F0 15 59 76 19 D3 5D B7 E3 9E BB F3 DF BF" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Special Values: _rid property + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.String("q4sGAMXtnQ0CAAAAAAAAAg=="), + }; + + string[] expectedText = + { + @"""q4sGAMXtnQ0CAAAAAAAAAg==""" + }; + + string[] expectedBinary1 = + { + "00000000 80 98 71 34 73 47 41 4D 58 74 6E 51 30 43 41 41", + "00000010 41 41 41 41 41 41 41 67 3D 3D" + }; + + string[] expectedBinary2 = + { + "00000000 80 71 06 02 AB 8B 06 00 C5 ED 9D 0D 02 00 00 00", + "00000010 00 00 00 02" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Object property names and values + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ObjectStart(), + JsonToken.FieldName("9mGv2f6n4k9i5h3b7u9a4z1n=="), + JsonToken.String("Redmond WA"), + JsonToken.FieldName("a6k3h2b7m9a5r0o9g8k6q0n4="), + JsonToken.String("a6k3h2b7m9a5r0o9g8k6q0n4="), + JsonToken.FieldName("TWljcm9zb2Z0IEF6dXJlIENsb3U="), + JsonToken.String("q4sGAMXtnQ0CAAAAAAAAAg=="), + JsonToken.FieldName("q4sGAMXtnQ0CAAAAAAAAAg=="), + JsonToken.String("Dallas TX"), + JsonToken.ObjectEnd(), + }; + + string[] expectedText = + { + @"{""9mGv2f6n4k9i5h3b7u9a4z1n=="":""Redmond WA"",""a6k3h2b7m9a5r0o9g8k6q0n4="":""a6k3h2b7m9a5r0o9g8k6q0n4="",""", + @"TWljcm9zb2Z0IEF6dXJlIENsb3U="":""q4sGAMXtnQ0CAAAAAAAAAg=="",""q4sGAMXtnQ0CAAAAAAAAAg=="":""Dallas TX""}" + }; + + string[] expectedBinary1 = + { + "00000000 80 EA 84 9A 39 6D 47 76 32 66 36 6E 34 6B 39 69", + "00000010 35 68 33 62 37 75 39 61 34 7A 31 6E 3D 3D 8A 52", + "00000020 65 64 6D 6F 6E 64 20 57 41 99 61 36 6B 33 68 32", + "00000030 62 37 6D 39 61 35 72 30 6F 39 67 38 6B 36 71 30", + "00000040 6E 34 3D C3 29 9C 54 57 6C 6A 63 6D 39 7A 62 32", + "00000050 5A 30 49 45 46 36 64 58 4A 6C 49 45 4E 73 62 33", + "00000060 55 3D 98 71 34 73 47 41 4D 58 74 6E 51 30 43 41", + "00000070 41 41 41 41 41 41 41 41 67 3D 3D C3 62 89 44 61", + "00000080 6C 6C 61 73 20 54 58" + }; + + string[] expectedBinary2 = + { + "00000000 80 EA 95 9A 39 6D 47 76 32 66 36 6E 34 6B 39 69", + "00000010 35 68 33 62 37 75 39 61 34 7A 31 6E 3D 3D 8A 52", + "00000020 65 64 6D 6F 6E 64 20 57 41 99 61 36 6B 33 68 32", + "00000030 62 37 6D 39 61 35 72 30 6F 39 67 38 6B 36 71 30", + "00000040 6E 34 3D C3 29 9C 54 57 6C 6A 63 6D 39 7A 62 32", + "00000050 5A 30 49 45 46 36 64 58 4A 6C 49 45 4E 73 62 33", + "00000060 55 3D 71 06 02 AB 8B 06 00 C5 ED 9D 0D 02 00 00", + "00000070 00 00 00 00 02 98 71 34 73 47 41 4D 58 74 6E 51", + "00000080 30 43 41 41 41 41 41 41 41 41 41 67 3D 3D 89 44", + "00000090 61 6C 6C 61 73 20 54 58" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Malformed: Missing padding + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ArrayStart(), + JsonToken.String("AtBughWl9PS9ax1B1NGkwt48Xfmy9g=="), + JsonToken.String("AtBughWl9PS9ax1B1NGkwt48Xfmy9g="), + JsonToken.String("AtBughWl9PS9ax1B1NGkwt48Xfmy9g"), + JsonToken.ArrayEnd(), + }; + + string[] expectedText = + { + @"[""AtBughWl9PS9ax1B1NGkwt48Xfmy9g=="",""AtBughWl9PS9ax1B1NGkwt48Xfmy9g="",""AtBughWl9PS9ax1B1NGkwt48Xfmy9", + @"g""]" + }; + + string[] expectedBinary1 = + { + "00000000 80 E2 60 A0 41 74 42 75 67 68 57 6C 39 50 53 39", + "00000010 61 78 31 42 31 4E 47 6B 77 74 34 38 58 66 6D 79", + "00000020 39 67 3D 3D 9F 41 74 42 75 67 68 57 6C 39 50 53", + "00000030 39 61 78 31 42 31 4E 47 6B 77 74 34 38 58 66 6D", + "00000040 79 39 67 3D 9E 41 74 42 75 67 68 57 6C 39 50 53", + "00000050 39 61 78 31 42 31 4E 47 6B 77 74 34 38 58 66 6D", + "00000060 79 39 67" + }; + + string[] expectedBinary2 = + { + "00000000 80 E2 58 71 08 02 02 D0 6E 82 15 A5 F4 F4 BD 6B", + "00000010 1D 41 D4 D1 A4 C2 DE 3C 5D F9 B2 F6 9F 41 74 42", + "00000020 75 67 68 57 6C 39 50 53 39 61 78 31 42 31 4E 47", + "00000030 6B 77 74 34 38 58 66 6D 79 39 67 3D 9E 41 74 42", + "00000040 75 67 68 57 6C 39 50 53 39 61 78 31 42 31 4E 47", + "00000050 6B 77 74 34 38 58 66 6D 79 39 67" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary1, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary2, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Malformed: Extra padding + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ArrayStart(), + JsonToken.String("1z9d6l4p2k7i5m1n8j3c7j4k9f2l"), + JsonToken.String("1z9d6l4p2k7i5m1n8j3c7j4k9f2l="), + JsonToken.String("1z9d6l4p2k7i5m1n8j3c7j4k9f2l=="), + JsonToken.ArrayEnd(), + }; + + string[] expectedText = + { + @"[""1z9d6l4p2k7i5m1n8j3c7j4k9f2l"",""1z9d6l4p2k7i5m1n8j3c7j4k9f2l="",""1z9d6l4p2k7i5m1n8j3c7j4k9f2l==""]" + }; + + string[] expectedBinary = + { + "00000000 80 E2 5A 9C 31 7A 39 64 36 6C 34 70 32 6B 37 69", + "00000010 35 6D 31 6E 38 6A 33 63 37 6A 34 6B 39 66 32 6C", + "00000020 9D 31 7A 39 64 36 6C 34 70 32 6B 37 69 35 6D 31", + "00000030 6E 38 6A 33 63 37 6A 34 6B 39 66 32 6C 3D 9E 31", + "00000040 7A 39 64 36 6C 34 70 32 6B 37 69 35 6D 31 6E 38", + "00000050 6A 33 63 37 6A 34 6B 39 66 32 6C 3D 3D" + }; + + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Malformed: Includes invalid characters + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ArrayStart(), + JsonToken.String("B8u6p9c3a6r7m1e8d4v0n9z5f2l3k4o7*"), + JsonToken.String("B8u6p9c3a6r*m1e8d4v0n9z5f2l3k4o7="), + JsonToken.String("*8u6p9c3a6r7m1e8d4v0n9z5f2l3k4o7="), + JsonToken.ArrayEnd(), + }; + + string[] expectedText = + { + @"[""B8u6p9c3a6r7m1e8d4v0n9z5f2l3k4o7*"",""B8u6p9c3a6r*m1e8d4v0n9z5f2l3k4o7="",""*8u6p9c3a6r7m1e8d4v0n9z5f2", + @"l3k4o7=""]" + }; + + string[] expectedBinary = + { + "00000000 80 E2 66 A1 42 38 75 36 70 39 63 33 61 36 72 37", + "00000010 6D 31 65 38 64 34 76 30 6E 39 7A 35 66 32 6C 33", + "00000020 6B 34 6F 37 2A A1 42 38 75 36 70 39 63 33 61 36", + "00000030 72 2A 6D 31 65 38 64 34 76 30 6E 39 7A 35 66 32", + "00000040 6C 33 6B 34 6F 37 3D A1 2A 38 75 36 70 39 63 33", + "00000050 61 36 72 37 6D 31 65 38 64 34 76 30 6E 39 7A 35", + "00000060 66 32 6C 33 6B 34 6F 37 3D" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary, JsonWriteOptions.EnableBase64Strings); + } + + // -------------------------------------- + // Malformed: Incorrectly truncated + // -------------------------------------- + { + JsonToken[] tokensToWrite = + { + JsonToken.ArrayStart(), + JsonToken.String("bbi4HWG/pwvvcLKJfWqrOguE4p/sFte="), + JsonToken.String("4x9h7c6m8k4p7i5m1n8j3c7j4k9fzx=="), + JsonToken.String("HGbeioIfU1C5U0MQloI07mg5MpQPwmzwoLUbOGO3I9CiASJgeSMocqq6ZfXbwAB2fdisq2V4NfjLP19KSBWF7joEpZRon4R1G7AudOL2+PVA+dIgvr=="), + JsonToken.ArrayEnd(), + }; + + string[] expectedText = + { + @"[""bbi4HWG/pwvvcLKJfWqrOguE4p/sFte="",""4x9h7c6m8k4p7i5m1n8j3c7j4k9fzx=="",""HGbeioIfU1C5U0MQloI07mg5MpQP", + @"wmzwoLUbOGO3I9CiASJgeSMocqq6ZfXbwAB2fdisq2V4NfjLP19KSBWF7joEpZRon4R1G7AudOL2+PVA+dIgvr==""]" + }; + + string[] expectedBinary = + { + "00000000 80 E2 AA A0 62 62 69 34 48 57 47 2F 70 77 76 76", + "00000010 63 4C 4B 4A 66 57 71 72 4F 67 75 45 34 70 2F 73", + "00000020 46 74 65 3D A0 34 78 39 68 37 63 36 6D 38 6B 34", + "00000030 70 37 69 35 6D 31 6E 38 6A 33 63 37 6A 34 6B 39", + "00000040 66 7A 78 3D 3D 7E 74 C8 A3 B8 9C 7E 27 CD D5 D8", + "00000050 B0 56 85 35 A3 EC 77 12 76 6B 9F 6B 4D 78 14 7A", + "00000060 6F EB EF 6F 66 55 FC 3C 3E 67 C9 DC 30 1D 9C 2A", + "00000070 CF E5 69 F3 3D 8E C7 6D 5A 33 56 7C 0F 0A 65 66", + "00000080 72 7A 1E 97 59 69 4E B3 9A 09 8D E5 96 53 E1 D5", + "00000090 78 53 BF 8B 70 AD F4 ED A6 49 63 C7 5B B0 4E 7E", + "000000A0 32 65 2B A8 35 B8 22 27 CF 76 79 AF 07" + }; + + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary, JsonWriteOptions.None); + ExecuteAndValidate(tokensToWrite, expectedText, expectedBinary, JsonWriteOptions.EnableBase64Strings); + } + } #endregion #region Array @@ -8862,7 +10102,7 @@ private static void ExecuteAndValidate( } } - if(!skipRoundTripTest) + if (!skipRoundTripTest) { SerializationSpec[] serializationSpecs = new SerializationSpec[] { @@ -8891,12 +10131,23 @@ private static void ExecuteAndValidate( RoundTripResult roundTripResult = null; foreach (RewriteScenario rewriteScenario in rewriteScenarios) { - // For Binary(EnableNumberArrays) to Binary(EnableNumberArrays) we need - // to relax the strict comparison since the logic of converting a regular - // array into a uniform number array might be different from the input. - bool strictComparison = !object.ReferenceEquals(inputSpec, outputSpec) || - (inputSpec.SerializationFormat != JsonSerializationFormat.Binary) || - (inputSpec.WriteOptions != JsonWriteOptions.EnableNumberArrays); + bool strictComparison = true; + + // For Binary+NumberArrays → Binary+NumberArrays rewrites, relax strict + // comparison, since the conversion logic from a regular array to a uniform + // number array may differ from the input representation. + if (inputSpec.IsBinary && inputSpec.EnablesNumberArrays && outputSpec.IsBinary && outputSpec.EnablesNumberArrays) + { + strictComparison = false; + } + + // Disable strict comparison for Binary → Binary+Base64 rewrites. + // In this case, already-compressed strings (e.g., 7-bit packed characters) + // are copied as-is without being re-encoded as Base64. + if (inputSpec.IsBinary && outputSpec.IsBinary && outputSpec.EnablesBase64Strings) + { + strictComparison = false; + } RoundTripBaseline roundTripBaseline = roundTripResult != null ? new RoundTripBaseline(roundTripResult.OutputResult, strictComparison) : null; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/NewtonsoftInteropTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/NewtonsoftInteropTests.cs index 42ef816e19..8b4a74a7bb 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/NewtonsoftInteropTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/NewtonsoftInteropTests.cs @@ -203,6 +203,7 @@ public void AllPrimitivesObjectTest() new JProperty("boolean", true), new JProperty("null", null), new JProperty("datetime", DateTime.Parse("2526-07-11T18:18:16.4520716")), + new JProperty("datetimeoffset", DateTimeOffset.Parse("2500-11-12T01:21:00+00:00")), new JProperty("spatialPoint", new JObject( new JProperty("type", "Point"), new JProperty("coordinate", new double[] { 118.9897, -46.6781 }))), @@ -272,6 +273,73 @@ public void AllDatetimeVariationsTest() NewtonsoftInteropTests.VerifyNewtonsoftInterop(jsonObject); } + [TestMethod] + [Owner("mpalaparthi")] + public void AllDateTimeOffsetVariationsTest() + { + // Arrange + JObject jsonObject = new JObject(); + + // 1. Current UTC DateTimeOffset using JProperty + DateTimeOffset utcNow = DateTimeOffset.UtcNow; + JProperty utcNowProperty = new JProperty("currentUtcDateTimeOffset", utcNow); + jsonObject.Add(utcNowProperty); + + // 2. Current Local DateTimeOffset using JProperty + DateTimeOffset localNow = DateTimeOffset.Now; + JProperty localNowProperty = new JProperty("currentLocalDateTimeOffset", localNow); + jsonObject.Add(localNowProperty); + + // 3. Date Only (formatted as string) using JProperty + DateTimeOffset dateOnly = new DateTimeOffset(2023, 10, 26, 0, 0, 0, TimeSpan.Zero); + JProperty dateOnlyProperty = new JProperty("specificDateOnlyOffset", dateOnly.ToString("yyyy-MM-dd")); + jsonObject.Add(dateOnlyProperty); + + // 4. Time Only (formatted as string) using JProperty + DateTimeOffset timeOnly = new DateTimeOffset(1, 1, 1, 14, 30, 0, TimeSpan.Zero); + JProperty timeOnlyProperty = new JProperty("specificTimeOnlyOffset", timeOnly.ToString("HH:mm:ss")); + jsonObject.Add(timeOnlyProperty); + + // 5. DateTimeOffset with milliseconds using JProperty + DateTimeOffset preciseDateTimeOffset = new DateTimeOffset(2024, 5, 29, 10, 15, 30, 123, TimeSpan.FromHours(5.5)); + JProperty preciseDateTimeOffsetProperty = new JProperty("preciseDateTimeOffset", preciseDateTimeOffset); + jsonObject.Add(preciseDateTimeOffsetProperty); + + // 6. DateTimeOffset in ISO 8601 format (UTC) using JProperty + DateTimeOffset isoUtcDateTimeOffset = new DateTimeOffset(2025, 1, 15, 8, 0, 0, TimeSpan.Zero); + JProperty isoUtcDateTimeOffsetProperty = new JProperty("isoUtcDateTimeOffset", isoUtcDateTimeOffset.ToString("o", CultureInfo.InvariantCulture)); + jsonObject.Add(isoUtcDateTimeOffsetProperty); + + // 7. DateTimeOffset in custom format using JProperty + DateTimeOffset customFormattedDateTimeOffset = new DateTimeOffset(2022, 7, 1, 9, 45, 10, TimeSpan.FromHours(-4)); + JProperty customFormattedDateTimeOffsetProperty = new JProperty("customFormattedDateTimeOffset", customFormattedDateTimeOffset.ToString("MM/dd/yyyy HH:mm:ss zzz")); + jsonObject.Add(customFormattedDateTimeOffsetProperty); + + // 8. Nullable DateTimeOffset (representing a missing or optional date) using JProperty + DateTimeOffset? nullableDateTimeOffset = null; + JProperty nullableDateTimeOffsetProperty = new JProperty("nullableDateTimeOffset", nullableDateTimeOffset); + jsonObject.Add(nullableDateTimeOffsetProperty); + + // 9. MinValue DateTimeOffset using JProperty + JProperty minDateTimeOffsetProperty = new JProperty("minDateTimeOffset", DateTimeOffset.MinValue); + jsonObject.Add(minDateTimeOffsetProperty); + + // 10. MaxValue DateTimeOffset using JProperty + JProperty maxDateTimeOffsetProperty = new JProperty("maxDateTimeOffset", DateTimeOffset.MaxValue); + jsonObject.Add(maxDateTimeOffsetProperty); + + // 11. Exact DateTimeOffset using JProperty + JProperty exactDateTimeOffsetProperty = new JProperty("exactDateTimeOffset", DateTimeOffset.Parse("2025-03-26T20:22:20+02:00")); + jsonObject.Add(exactDateTimeOffsetProperty); + + // 12. Various time zone offsets + jsonObject.Add(new JProperty("positiveOffset", new DateTimeOffset(2025, 10, 26, 12, 0, 0, TimeSpan.FromHours(5.5)))); + jsonObject.Add(new JProperty("negativeOffset", new DateTimeOffset(2025, 10, 26, 12, 0, 0, TimeSpan.FromHours(-8)))); + jsonObject.Add(new JProperty("uncommonOffset", new DateTimeOffset(2025, 10, 26, 12, 0, 0, new TimeSpan(12, 45, 0)))); + + NewtonsoftInteropTests.VerifyNewtonsoftInterop(jsonObject); + } + public enum Day { Sun, Mon, Tue, Wed, Thu, Fri, Sat }; public sealed class ObjectWithAttributes @@ -437,4 +505,4 @@ public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectTy } } } -} \ No newline at end of file +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/SystemTextJsonSerializerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/SystemTextJsonSerializerTests.cs new file mode 100644 index 0000000000..3019ec8d34 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Json/SystemTextJsonSerializerTests.cs @@ -0,0 +1,72 @@ +namespace Microsoft.Azure.Cosmos.Tests.Json +{ + using System.IO; + using System.Text.Json; + using Microsoft.Azure.Cosmos.Services.Management.Tests; + using Microsoft.Azure.Cosmos.Tests.Poco.STJ; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + /// + /// Tests for the serializer that uses . + /// + [TestClass] + public sealed class SystemTextJsonSerializerTests + { + private SystemTextJsonSerializer systemTextJsonSerializer; + + [TestInitialize] + public void SetUp() + { + this.systemTextJsonSerializer = new SystemTextJsonSerializer(new JsonSerializerOptions()); + } + + [TestMethod] + public void TestPolymorphicSerialization_IncludesTypeDiscriminator() + { + // Arrange. + Shape circle = new Circle + { + Id = "circle", + Color = "Red", + Radius = 5.0 + }; + + // Act. + Stream serializedStream = this.systemTextJsonSerializer.ToStream(circle); + using StreamReader reader = new(serializedStream); + string json = reader.ReadToEnd(); + + // Assert. + using JsonDocument jsonDocument = JsonDocument.Parse(json); + JsonElement rootElement = jsonDocument.RootElement; + + Assert.AreEqual("Circle", rootElement.GetProperty("$type").GetString()); + Assert.AreEqual(5.0, rootElement.GetProperty("radius").GetDouble()); + } + + [TestMethod] + public void TestPolymorphicSerialization_SerializeDeserialize_PreservesType() + { + // Arrange. + Shape original = new Circle + { + Id = "circle", + Color = "Green", + Radius = 7.5 + }; + + // Act. + Stream serializedStream = this.systemTextJsonSerializer.ToStream(original); + Shape deserialized = this.systemTextJsonSerializer.FromStream(serializedStream); + + // Assert. + Assert.IsNotNull(deserialized); + Assert.IsInstanceOfType(deserialized, typeof(Circle)); + + Circle deserializedCircle = (Circle)deserialized; + Assert.AreEqual(original.Id, deserializedCircle.Id); + Assert.AreEqual(original.Color, deserializedCircle.Color); + Assert.AreEqual(((Circle)original).Radius, deserializedCircle.Radius); + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/LocationCacheTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/LocationCacheTests.cs index ab0176ed47..b8f85d4bef 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/LocationCacheTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/LocationCacheTests.cs @@ -143,7 +143,7 @@ private async Task ValidateRetryOnSessionNotAvailableWithEndpointDiscoveryDisabl useMultipleWriteLocations: useMultipleWriteLocations, enableEndpointDiscovery: enableEndpointDiscovery, isPreferredLocationsListEmpty: isPreferredLocationsListEmpty); - ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery, partitionLevelFailoverEnabled: false, endpointManager); + ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery, endpointManager); using (DocumentServiceRequest request = this.CreateRequest(isReadRequest: isReadRequest, isMasterResourceType: false)) { @@ -189,15 +189,13 @@ await BackoffRetryUtility.ExecuteAsync( private ClientRetryPolicy CreateClientRetryPolicy( bool enableEndpointDiscovery, - bool partitionLevelFailoverEnabled, GlobalEndpointManager endpointManager) { return new ClientRetryPolicy( endpointManager, this.partitionKeyRangeLocationCache, new RetryOptions(), - enableEndpointDiscovery, - isPartitionLevelFailoverEnabled: partitionLevelFailoverEnabled, + enableEndpointDiscovery, false); } @@ -220,7 +218,7 @@ private async Task ValidateRetryOnSessionNotAvailableWithDisableMultipleWriteLoc isPreferredLocationsListEmpty: isPreferredLocationsListEmpty); endpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(this.databaseAccount); - ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery, partitionLevelFailoverEnabled: false, endpointManager); + ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery, endpointManager); using (DocumentServiceRequest request = this.CreateRequest(isReadRequest: true, isMasterResourceType: false)) { @@ -304,7 +302,7 @@ private async Task ValidateRetryOnReadSessionNotAvailableWithEnableMultipleWrite isDefaultEndpointARegionalEndpoint: isDefaultEndpointARegionalEndpoint); endpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(this.databaseAccount); - ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery, partitionLevelFailoverEnabled: false, endpointManager); + ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery, endpointManager); if (!isPreferredLocationsEmpty) { @@ -533,7 +531,7 @@ private async Task ValidateRetryOnWriteSessionNotAvailableWithEnableMultipleWrit isDefaultEndpointARegionalEndpoint: isDefaultEndpointARegionalEndpoint); endpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(this.databaseAccount); - ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery, partitionLevelFailoverEnabled: false, endpointManager); + ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery, endpointManager); if (!isPreferredLocationsEmpty) { @@ -750,7 +748,7 @@ public async Task ValidateRetryOnWriteForbiddenExceptionAsync(bool isPreferredLo isDefaultEndpointARegionalEndpoint: isDefaultEndpointARegionalEndpoint); endpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(this.databaseAccount); - ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery: true, partitionLevelFailoverEnabled: false, endpointManager: endpointManager); + ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery: true, endpointManager: endpointManager); if (isPreferredLocationsEmpty) { @@ -894,7 +892,7 @@ private async Task ValidateRetryOnDatabaseAccountNotFoundAsync(bool enableMultip } endpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(this.databaseAccount); - ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery: true, partitionLevelFailoverEnabled: false, endpointManager: endpointManager); + ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery: true, endpointManager: endpointManager); int expectedRetryCount = isReadRequest || enableMultipleWriteLocations ? 2 : 1; @@ -1047,7 +1045,7 @@ private async Task ValidateRetryOnHttpExceptionAsync(bool enableMultipleWriteLoc isDefaultEndpointARegionalEndpoint: isDefaultEndpointARegionalEndpoint); endpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(this.databaseAccount); - ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery: true, partitionLevelFailoverEnabled: false, endpointManager: endpointManager); + ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery: true, endpointManager: endpointManager); if (isPreferredLocationsEmpty) { @@ -1219,7 +1217,7 @@ public async Task ClientRetryPolicy_ValidateRetryOnServiceUnavailable( endpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(this.databaseAccount); - ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery, partitionLevelFailoverEnabled: enablePartitionLevelFailover, endpointManager); + ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery, endpointManager); if (!usesPreferredLocations) { @@ -1398,7 +1396,7 @@ public void VerifyRegionExcludedTest( isDefaultEndpointARegionalEndpoint: isDefaultEndpointAlsoRegionEndpoint); endpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(this.databaseAccount); - ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery: true, partitionLevelFailoverEnabled: false, endpointManager: endpointManager); + ClientRetryPolicy retryPolicy = this.CreateClientRetryPolicy(enableEndpointDiscovery: true, endpointManager: endpointManager); using (DocumentServiceRequest request = this.CreateRequest(isReadRequest: isReadRequest, isMasterResourceType: false)) { @@ -1728,7 +1726,10 @@ private GlobalEndpointManager Initialize( GlobalEndpointManager endpointManager = new GlobalEndpointManager(this.mockedClient.Object, connectionPolicy); this.partitionKeyRangeLocationCache = enablePartitionLevelFailover - ? new GlobalPartitionEndpointManagerCore(endpointManager) + ? new GlobalPartitionEndpointManagerCore( + endpointManager, + isPartitionLevelFailoverEnabled: true, + isPartitionLevelCircuitBreakerEnabled: true) : GlobalPartitionEndpointManagerNoOp.Instance; return endpointManager; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj index f441862c30..cb25b414f2 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj @@ -1,440 +1,435 @@ - - - true - - - - AnyCPU - net6.0 - false - false - Microsoft.Azure.Cosmos.Tests - $(LangVersion) - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - Always - - - Always - - - Always - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - Always - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - - + + + true + + + + AnyCPU + net6.0 + false + false + Microsoft.Azure.Cosmos.Tests + $(LangVersion) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + compile + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + Always + + + Always + + + Always + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + Always + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + PreserveNewest - - - - - PreserveNewest - - - - - - PreserveNewest - - - - - - PreserveNewest - - - - - true - true - ..\..\..\testkey.snk - - - - + + + + + PreserveNewest + + + + + true + true + ..\..\..\testkey.snk + + + diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Pagination/ParallelPrefetchTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Pagination/ParallelPrefetchTests.cs index 3a5f5459d9..72147aea1c 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Pagination/ParallelPrefetchTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Pagination/ParallelPrefetchTests.cs @@ -459,6 +459,8 @@ private sealed class SimpleTrace : ITrace public IReadOnlyDictionary Data { get; } = new Dictionary(); + public bool IsBeingWalked => true; // needs to return true to allow materialization + internal SimpleTrace(ITrace parent, string name, TraceComponent component, Cosmos.Tracing.TraceLevel level) { this.Parent = parent; @@ -509,6 +511,12 @@ public ITrace StartChild(string name, TraceComponent component, Cosmos.Tracing.T return child; } + + bool ITrace.TryGetDatum(string key, out object datum) + { + datum = null; + return false; + } } /// diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeCacheTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeCacheTest.cs index 41b53d293a..6f47751e42 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeCacheTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeCacheTest.cs @@ -98,7 +98,7 @@ public async Task TryGetOverlappingRangesAsync_WithFreshContainer_ShouldNotAddSa .Returns(new ValueTask(authToken)); // Act. - PartitionKeyRangeCache partitionKeyRangeCache = new(mockTokenProvider.Object, mockStoreModel.Object, mockCollectioNCache.Object, endpointManager, enableAsyncCacheExceptionNoSharing: false); + PartitionKeyRangeCache partitionKeyRangeCache = new(mockTokenProvider.Object, mockStoreModel.Object, mockCollectioNCache.Object, endpointManager, useLengthAwareRangeComparer: false, enableAsyncCacheExceptionNoSharing: false); IReadOnlyList partitionKeyRanges = await partitionKeyRangeCache.TryGetOverlappingRangesAsync( containerRId, FeedRangeEpk.FullRange.Range, @@ -207,7 +207,7 @@ public async Task TryGetOverlappingRangesAsync_WhenGatewayThrowsServiceUnavailab mockTokenProvider.Setup(x => x.GetUserAuthorizationTokenAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(new ValueTask(authToken)); - PartitionKeyRangeCache partitionKeyRangeCache = new(mockTokenProvider.Object, mockStoreModel.Object, mockCollectioNCache.Object, mockedEndpointManager.Object, enableAsyncCacheExceptionNoSharing: false); + PartitionKeyRangeCache partitionKeyRangeCache = new(mockTokenProvider.Object, mockStoreModel.Object, mockCollectioNCache.Object, mockedEndpointManager.Object, useLengthAwareRangeComparer: false, enableAsyncCacheExceptionNoSharing: false); if (shouldSucceed) { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs index 05f82bec36..9eeb59e908 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs @@ -16,6 +16,7 @@ namespace Microsoft.Azure.Cosmos.Tests using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Diagnostics; using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -301,13 +302,14 @@ public async Task ReadItemAsync_WithThinClientEnabledAndServiceUnavailableReceiv containerName: containerName, containerRid: containerRid); - if (enablePartitionLevelFailover) + bool isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); + if (enablePartitionLevelFailover || isThinClientEnabled) { MockSetupsHelper.SetupPartitionKeyRanges( - mockHttpHandler: mockHttpHandler, - regionEndpoint: primaryRegionEndpoint, - containerResourceId: containerResourceId, - partitionKeyRangeIds: out IReadOnlyList secondaryRegionPartitionKeyRangeIds); + mockHttpHandler: mockHttpHandler, + regionEndpoint: primaryRegionEndpoint, + containerResourceId: containerResourceId, + partitionKeyRangeIds: out IReadOnlyList secondaryRegionPartitionKeyRangeIds); } CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() @@ -344,6 +346,11 @@ public async Task ReadItemAsync_WithThinClientEnabledAndServiceUnavailableReceiv CosmosTraceDiagnostics traceDiagnostic = readResponse.Diagnostics as CosmosTraceDiagnostics; Assert.IsNotNull(traceDiagnostic); + if (traceDiagnostic.Value is Trace rootLevelTrace) + { + rootLevelTrace.SetWalkingStateRecursively(); + } + traceDiagnostic.Value.Data.TryGetValue("Hedge Context", out object hedgeContext); if (enablePartitionLevelFailover) @@ -451,6 +458,12 @@ public async Task ReadItemAsync_WithThinClientEnabledAndHttpRequestExceptionRece containerName: containerName, containerRid: containerRid); + MockSetupsHelper.SetupPartitionKeyRanges( + mockHttpHandler: mockHttpHandler, + regionEndpoint: primaryRegionEndpoint, + containerResourceId: containerResourceId, + partitionKeyRangeIds: out IReadOnlyList partitionKeyRangeIds); + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() { ConsistencyLevel = Cosmos.ConsistencyLevel.Strong, @@ -601,6 +614,12 @@ public async Task CreateItemAsync_WithThinClientEnabledAndServiceUnavailableRece containerName: containerName, containerRid: containerRid); + MockSetupsHelper.SetupPartitionKeyRanges( + mockHttpHandler: mockHttpHandler, + regionEndpoint: primaryRegionEndpoint, + containerResourceId: containerResourceId, + partitionKeyRangeIds: out IReadOnlyList partitionKeyRangeIds); + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() { ConsistencyLevel = Cosmos.ConsistencyLevel.Strong, @@ -638,6 +657,11 @@ public async Task CreateItemAsync_WithThinClientEnabledAndServiceUnavailableRece CosmosTraceDiagnostics traceDiagnostic = createItemResponse.Diagnostics as CosmosTraceDiagnostics; Assert.IsNotNull(traceDiagnostic); + if (traceDiagnostic.Value is Trace rootLevelTrace) + { + rootLevelTrace.SetWalkingStateRecursively(); + } + traceDiagnostic.Value.Data.TryGetValue("Hedge Context", out object hedgeContext); Assert.IsNull(hedgeContext); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeHandlerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeHandlerTests.cs index e2a83f73aa..e7e95afbfc 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeHandlerTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeHandlerTests.cs @@ -660,13 +660,14 @@ public async Task PartitionKeyRangeGoneTracePlumbingTest() collectionCache.Setup(c => c.ResolveCollectionAsync(It.IsAny(), default, trace)) .ReturnsAsync(containerProperties); - CollectionRoutingMap collectionRoutingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap(new List>(), collectionRid); + CollectionRoutingMap collectionRoutingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap(new List>(), collectionRid, false); Mock partitionKeyRangeCache = new Mock( MockBehavior.Strict, new Mock().Object, new Mock().Object, collectionCache.Object, endpointManager, + false, false); partitionKeyRangeCache.Setup(c => c.TryLookupAsync(collectionRid, null, It.IsAny(), trace)) .ReturnsAsync(collectionRoutingMap); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Poco/STJ/PolymorphicTypes.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Poco/STJ/PolymorphicTypes.cs new file mode 100644 index 0000000000..27b4dbd0e0 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Poco/STJ/PolymorphicTypes.cs @@ -0,0 +1,92 @@ +namespace Microsoft.Azure.Cosmos.Tests.Poco.STJ +{ + using System; + using System.Text.Json; + using System.Text.Json.Serialization; + + // Note: [JsonPolymorphic] and [JsonDerivedType] attributes require .NET 7+. + // Since this test project targets .NET 6, we use a custom JsonConverter approach instead. + // The converter is registered on the base type (Shape) and writes a "$type" discriminator, + // achieving the same polymorphic serialization behavior. + + [JsonConverter(typeof(ShapeConverter))] + public abstract class Shape + { + [JsonPropertyName("id")] + public string Id { get; set; } + + [JsonPropertyName("color")] + public string Color { get; set; } + } + + public class Circle : Shape + { + [JsonPropertyName("radius")] + public double Radius { get; set; } + } + + /// + /// Custom converter that writes a type discriminator for polymorphic serialization. + /// This converter is invoked when serializing through the base type (Shape), + /// which only happens when typeof(T) is used instead of input.GetType(). + /// + public class ShapeConverter : JsonConverter + { + public override Shape Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using JsonDocument doc = JsonDocument.ParseValue(ref reader); + JsonElement root = doc.RootElement; + + string shapeType = root.TryGetProperty("$type", out JsonElement typeElement) + ? typeElement.GetString() + : null; + + Shape result; + if (shapeType == nameof(Circle) || root.TryGetProperty("radius", out _)) + { + result = new Circle + { + Radius = root.TryGetProperty("radius", out JsonElement radiusEl) ? radiusEl.GetDouble() : 0 + }; + } + else + { + throw new JsonException("Cannot determine shape type"); + } + + result.Id = root.TryGetProperty("id", out JsonElement idEl) ? idEl.GetString() : null; + result.Color = root.TryGetProperty("color", out JsonElement colorEl) ? colorEl.GetString() : null; + + return result; + } + + public override void Write(Utf8JsonWriter writer, Shape value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + // Write type discriminator for polymorphic deserialization + if (value is Circle) + { + writer.WriteString("$type", nameof(Circle)); + } + + // Write base properties + if (value.Id != null) + { + writer.WriteString("id", value.Id); + } + if (value.Color != null) + { + writer.WriteString("color", value.Color); + } + + // Write derived properties + if (value is Circle circle) + { + writer.WriteNumber("radius", circle.Radius); + } + + writer.WriteEndObject(); + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs index b751ac3dc8..91d8c0f95a 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs @@ -1030,6 +1030,7 @@ private static IQueryPipelineStage CreateQueryPipelineStage( enableOptimisticDirectExecution: queryRequestOptions.EnableOptimisticDirectExecution, isHybridSearchQueryPlanOptimizationDisabled: queryRequestOptions.IsHybridSearchQueryPlanOptimizationDisabled, enableDistributedQueryGatewayMode: queryRequestOptions.EnableDistributedQueryGatewayMode, + fullTextScoreScope: queryRequestOptions.FullTextScoreScope, testInjections: queryRequestOptions.TestSettings); List targetPkRanges = new(); @@ -1325,7 +1326,8 @@ public override Task GetCachedContainerQueryProperties }, new PartitionKeyDefinition(), vectorEmbeddingPolicy: null, - Cosmos.GeospatialType.Geometry)); + Cosmos.GeospatialType.Geometry, + false)); } public override Task GetClientDisableOptimisticDirectExecutionAsync() diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OrderByQueryPartitionRangePageAsyncEnumeratorTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OrderByQueryPartitionRangePageAsyncEnumeratorTests.cs index 6c189e92e7..c7b7d3dffb 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OrderByQueryPartitionRangePageAsyncEnumeratorTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OrderByQueryPartitionRangePageAsyncEnumeratorTests.cs @@ -1,76 +1,76 @@ -namespace Microsoft.Azure.Cosmos.Tests.Query -{ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using Cosmos.Pagination; - using Cosmos.Query.Core.Monads; - using Cosmos.Query.Core.Pipeline.CrossPartition.OrderBy; - using Cosmos.Query.Core.Pipeline.Pagination; - using Cosmos.Tracing; - using Microsoft.Azure.Cosmos.Tests.Query.Pipeline; - using Pagination; - using VisualStudio.TestTools.UnitTesting; - - [TestClass] - public class OrderByQueryPartitionRangePageAsyncEnumeratorTests - { - [TestMethod] - public async Task TestMoveNextAsyncThrowsTaskCanceledException() - { - Implementation implementation = new Implementation(); - await implementation.TestMoveNextAsyncThrowsTaskCanceledException(); - } - - [TestClass] - private sealed class Implementation : PartitionRangeEnumeratorTests - { - public Implementation() - : base(singlePartition: true) - { - } - - public override IReadOnlyList GetRecordsFromPage(OrderByQueryPage page) - { - throw new NotImplementedException(); - } - - protected override IAsyncEnumerable> CreateEnumerable( - IDocumentContainer documentContainer, - bool aggressivePrefetch = false, - QueryState state = null) - { - throw new NotImplementedException(); - } - - protected override Task>> CreateEnumeratorAsync( - IDocumentContainer documentContainer, - bool aggressivePrefetch = false, - bool exercisePrefetch = false, - QueryState state = null, - CancellationToken cancellationToken = default) - { - List ranges = documentContainer.GetFeedRangesAsync( - trace: NoOpTrace.Singleton, - cancellationToken: cancellationToken).Result; - Assert.AreEqual(1, ranges.Count); - - IAsyncEnumerator> enumerator = new TracingAsyncEnumerator>( - OrderByQueryPartitionRangePageAsyncEnumerator.Create( +namespace Microsoft.Azure.Cosmos.Tests.Query +{ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using Cosmos.Pagination; + using Cosmos.Query.Core.Monads; + using Cosmos.Query.Core.Pipeline.CrossPartition.OrderBy; + using Cosmos.Query.Core.Pipeline.Pagination; + using Cosmos.Tracing; + using Microsoft.Azure.Cosmos.Tests.Query.Pipeline; + using Pagination; + using VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class OrderByQueryPartitionRangePageAsyncEnumeratorTests + { + [TestMethod] + public async Task TestMoveNextAsyncThrowsTaskCanceledException() + { + Implementation implementation = new Implementation(); + await implementation.TestMoveNextAsyncThrowsTaskCanceledException(); + } + + [TestClass] + private sealed class Implementation : PartitionRangeEnumeratorTests + { + public Implementation() + : base(singlePartition: true) + { + } + + public override IReadOnlyList GetRecordsFromPage(OrderByQueryPage page) + { + throw new NotImplementedException(); + } + + protected override IAsyncEnumerable> CreateEnumerable( + IDocumentContainer documentContainer, + bool aggressivePrefetch = false, + QueryState state = null) + { + throw new NotImplementedException(); + } + + protected override Task>> CreateEnumeratorAsync( + IDocumentContainer documentContainer, + bool aggressivePrefetch = false, + bool exercisePrefetch = false, + QueryState state = null, + CancellationToken cancellationToken = default) + { + List ranges = documentContainer.GetFeedRangesAsync( + trace: NoOpTrace.Singleton, + cancellationToken: cancellationToken).Result; + Assert.AreEqual(1, ranges.Count); + + IAsyncEnumerator> enumerator = new TracingAsyncEnumerator>( + OrderByQueryPartitionRangePageAsyncEnumerator.Create( queryDataSource: documentContainer, - containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - sqlQuerySpec: new Cosmos.Query.Core.SqlQuerySpec("SELECT * FROM c"), - feedRangeState: new FeedRangeState(ranges[0], state), - partitionKey: null, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), + sqlQuerySpec: new Cosmos.Query.Core.SqlQuerySpec("SELECT * FROM c"), + feedRangeState: new FeedRangeState(ranges[0], state), + partitionKey: null, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), filter: "filter", - PrefetchPolicy.PrefetchSinglePage), + PrefetchPolicy.PrefetchSinglePage), NoOpTrace.Singleton, - cancellationToken); - - return Task.FromResult(enumerator); - } - } - } + cancellationToken); + + return Task.FromResult(enumerator); + } + } + } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FactoryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FactoryTests.cs index e7021a86f0..9111b1603e 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FactoryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FactoryTests.cs @@ -1,41 +1,42 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos.Tests.Query.Pipeline -{ - using System.Collections.Generic; - using Microsoft.Azure.Cosmos.Pagination; - using Microsoft.Azure.Cosmos.Query.Core; - using Microsoft.Azure.Cosmos.Query.Core.Monads; - using Microsoft.Azure.Cosmos.Query.Core.Pipeline; - using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination; - using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using Moq; - - [TestClass] - public class FactoryTests - { - [TestMethod] - public void TestCreate() - { - Mock mockDocumentContainer = new Mock(); - - TryCatch monadicCreatePipeline = PipelineFactory.MonadicCreate( - documentContainer: mockDocumentContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c"), - targetRanges: new List() { FeedRangeEpk.FullRange }, - partitionKey: null, +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Tests.Query.Pipeline +{ + using System.Collections.Generic; + using Microsoft.Azure.Cosmos.Pagination; + using Microsoft.Azure.Cosmos.Query.Core; + using Microsoft.Azure.Cosmos.Query.Core.Monads; + using Microsoft.Azure.Cosmos.Query.Core.Pipeline; + using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination; + using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + + [TestClass] + public class FactoryTests + { + [TestMethod] + public void TestCreate() + { + Mock mockDocumentContainer = new Mock(); + + TryCatch monadicCreatePipeline = PipelineFactory.MonadicCreate( + documentContainer: mockDocumentContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c"), + targetRanges: new List() { FeedRangeEpk.FullRange }, + partitionKey: null, containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - allRanges: new List() { FeedRangeEpk.FullRange }, + allRanges: new List() { FeedRangeEpk.FullRange }, queryInfo: new QueryInfo() { }, - hybridSearchQueryInfo: null, + hybridSearchQueryInfo: null, maxItemCount: 10, - isContinuationExpected: true, - maxConcurrency: 10, - requestContinuationToken: default); ; - Assert.IsTrue(monadicCreatePipeline.Succeeded); - } - } -} + isContinuationExpected: true, + maxConcurrency: 10, + fullTextScoreScope: FullTextScoreScope.Global, + requestContinuationToken: default); ; + Assert.IsTrue(monadicCreatePipeline.Succeeded); + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FullPipelineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FullPipelineTests.cs index 4ef83ab057..55d6146d9d 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FullPipelineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FullPipelineTests.cs @@ -1,387 +1,389 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos.Tests.Query.Pipeline -{ - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.IO; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.ChangeFeed.Pagination; - using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.Pagination; - using Microsoft.Azure.Cosmos.Query; - using Microsoft.Azure.Cosmos.Query.Core; - using Microsoft.Azure.Cosmos.Query.Core.ExecutionContext; - using Microsoft.Azure.Cosmos.Query.Core.Monads; - using Microsoft.Azure.Cosmos.Query.Core.Pipeline; - using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination; - using Microsoft.Azure.Cosmos.Query.Core.QueryClient; - using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; - using Microsoft.Azure.Cosmos.ReadFeed.Pagination; - using Microsoft.Azure.Cosmos.Tests.Pagination; - using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Documents; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using Moq; - using Newtonsoft.Json; - - [TestClass] - public class FullPipelineTests - { - internal static readonly PartitionKeyDefinition partitionKeyDefinition = new PartitionKeyDefinition() - { - Paths = new Collection() - { - "/pk" - }, - Kind = PartitionKind.Hash, - Version = PartitionKeyDefinitionVersion.V2, - }; - - [TestMethod] - public async Task TestMerge() - { - List documents = Enumerable - .Range(0, 100) - .Select(x => CosmosObject.Parse($"{{\"pk\" : {x} }}")) - .ToList(); - - MergeTestUtil mergeTest = new MergeTestUtil(); - mergeTest.DocumentContainer = await CreateDocumentContainerAsync( - documents: documents, - numSplits: 2, - failureConfigs: new FlakyDocumentContainer.FailureConfigs( - inject429s: false, - injectEmptyPages: false, - shouldReturnFailure: mergeTest.ShouldReturnFailure)); - - string query = "SELECT * FROM c ORDER BY c._ts"; - int pageSize = 10; - IQueryPipelineStage pipelineStage = await CreatePipelineAsync(mergeTest.DocumentContainer, query, pageSize); - - List elements = new List(); - int iteration = 0; - while (await pipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) - { - TryCatch tryGetQueryPage = pipelineStage.Current; - tryGetQueryPage.ThrowIfFailed(); - - elements.AddRange(tryGetQueryPage.Result.Documents); - ++iteration; - - if (iteration == 1) - { - mergeTest.ShouldMerge = MergeTestUtil.TriState.Ready; - } - } - - Assert.AreEqual(expected: documents.Count, actual: elements.Count); - } - - [TestMethod] - public async Task SelectStar() - { - List documents = new List(); - for (int i = 0; i < 250; i++) - { - documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); - } - - List documentsQueried = await ExecuteQueryAsync( - query: "SELECT * FROM c", - documents: documents); - - Assert.AreEqual(expected: documents.Count, actual: documentsQueried.Count); - } - - [TestMethod] - public async Task OrderBy() - { - List documents = new List(); - for (int i = 0; i < 250; i++) - { - documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); - } - - List documentsQueried = await ExecuteQueryAsync( - query: "SELECT * FROM c ORDER BY c._ts", - documents: documents); - - Assert.AreEqual(expected: documents.Count, actual: documentsQueried.Count); - } - - [TestMethod] - [Ignore] // Continuation token for in memory container needs to be updated to suppport this query - public async Task OrderByWithJoins() - { - List documents = new List() - { - CosmosObject.Parse($"{{\"pk\" : {1}, \"children\" : [\"Alice\", \"Bob\", \"Charlie\"]}}"), - CosmosObject.Parse($"{{\"pk\" : {2}, \"children\" : [\"Dave\", \"Eve\", \"Fancy\"]}}"), - CosmosObject.Parse($"{{\"pk\" : {3}, \"children\" : [\"George\", \"Henry\", \"Igor\"]}}"), - CosmosObject.Parse($"{{\"pk\" : {4}, \"children\" : [\"Jack\", \"Kim\", \"Levin\"]}}"), - }; - - List documentsQueried = await ExecuteQueryAsync( - query: "SELECT d FROM c JOIN d in c.children ORDER BY c.pk", - documents: documents, - pageSize: 2); - - Assert.AreEqual(expected: documents.Count * 3, actual: documentsQueried.Count); - } - - [TestMethod] - public async Task Top() - { - List documents = new List(); - for (int i = 0; i < 250; i++) - { - documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); - } - - List documentsQueried = await ExecuteQueryAsync( - query: "SELECT TOP 10 * FROM c", - documents: documents); - - Assert.AreEqual(expected: 10, actual: documentsQueried.Count); - } - - [TestMethod] - public async Task OffsetLimit() - { - List documents = new List(); - for (int i = 0; i < 250; i++) - { - documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); - } - - List documentsQueried = await ExecuteQueryAsync( - query: "SELECT * FROM c OFFSET 10 LIMIT 103", - documents: documents); - - Assert.AreEqual(expected: 103, actual: documentsQueried.Count); - } - - [TestMethod] - public async Task Aggregates() - { - const int DocumentCount = 250; - List documents = new List(); - for (int i = 0; i < DocumentCount; i++) - { - documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); - } - - List documentsQueried = await ExecuteQueryAsync( - query: "SELECT VALUE COUNT(1) FROM c", - documents: documents); - - Assert.AreEqual(expected: 1, actual: documentsQueried.Count); - if (documentsQueried[0] is CosmosNumber number) - { - Assert.AreEqual(expected: DocumentCount, actual: Number64.ToLong(number.Value)); - } - else - { - Assert.Fail(); - } - } - - [TestMethod] - public async Task DCount() - { - List documents = new List(); - for (int i = 0; i < 250; i++) - { - documents.Add(CosmosObject.Parse($"{{\"pk\" : {i}, \"val\": {i % 49} }}")); - } - - List documentsQueried = await ExecuteQueryAsync( - query: "SELECT VALUE COUNT(1) FROM (SELECT DISTINCT VALUE c.val FROM c)", - documents: documents); - - Assert.AreEqual(expected: 1, actual: documentsQueried.Count); - Assert.IsTrue(documentsQueried[0] is CosmosNumber); - CosmosNumber result = documentsQueried[0] as CosmosNumber; - Assert.AreEqual(expected: 49, actual: Number64.ToLong(result.Value)); - } - - [TestMethod] - [Ignore] - // Need to implement group by continuation token on the in memory collection. - public async Task GroupBy() - { - List documents = new List(); - for (int i = 0; i < 250; i++) - { - documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); - } - - List documentsQueried = await ExecuteQueryAsync( - query: "SELECT VALUE COUNT(1) FROM c GROUP BY c.pk", - documents: documents); - - Assert.AreEqual(expected: documents.Count, actual: documentsQueried.Count); - } - - [TestMethod] - public async Task Tracing() - { - List documents = new List(); - for (int i = 0; i < 250; i++) - { - documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); - } - - IDocumentContainer documentContainer = await CreateDocumentContainerAsync(documents); - IQueryPipelineStage pipelineStage = await CreatePipelineAsync(documentContainer, "SELECT * FROM c", pageSize: 10); - - Trace rootTrace; - int numTraces = (await documentContainer.GetFeedRangesAsync(NoOpTrace.Singleton, default)).Count; - using (rootTrace = Trace.GetRootTrace("Cross Partition Query")) - { - while (await pipelineStage.MoveNextAsync(rootTrace, cancellationToken: default)) - { - TryCatch tryGetQueryPage = pipelineStage.Current; - tryGetQueryPage.ThrowIfFailed(); - - numTraces++; - } - } - - Assert.AreEqual(numTraces, rootTrace.Children.Count); - } - - [TestMethod] - public async Task OffsetLimitPageSize() - { - List documents = new List(); - for (int i = 0; i < 1100; i++) - { - documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); - } - - MockInMemoryContainer mockInMemoryContainer = new MockInMemoryContainer(new InMemoryContainer(partitionKeyDefinition)); - DocumentContainer documentContainer = await CreateDocumentContainerAsync(documents, mockInMemoryContainer, numSplits: 4); - - // OFFSET/LIMIT with ORDER BY - await this.TestPageSizeAsync("SELECT c.pk FROM c ORDER BY c.pk OFFSET 0 LIMIT 500", expectedPageSize: 500, expectedResults: 500, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT c.pk FROM c ORDER BY c.pk OFFSET 10000 LIMIT 5000", expectedPageSize: 1000, expectedResults: 0, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT c.pk FROM c ORDER BY c.pk OFFSET 10 LIMIT 100", expectedPageSize: 110, expectedResults: 100, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT c.pk FROM c ORDER BY c.pk OFFSET 0 LIMIT 100", expectedPageSize: 100, expectedResults: 100, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT c.pk FROM c ORDER BY c.pk OFFSET 100 LIMIT 0", expectedPageSize: 100, expectedResults: 0, mockInMemoryContainer, documentContainer); - - // OFFSET/LIMIT without ORDER BY - await this.TestPageSizeAsync("SELECT c.pk FROM c OFFSET 10 LIMIT 100", expectedPageSize: 1000, expectedResults: 100, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT c.pk FROM c OFFSET 0 LIMIT 100", expectedPageSize: 1000, expectedResults: 100, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT c.pk FROM c OFFSET 100 LIMIT 0", expectedPageSize: 1000, expectedResults: 0, mockInMemoryContainer, documentContainer); - - // TOP with ORDER BY - await this.TestPageSizeAsync("SELECT TOP 5 c.pk FROM c ORDER BY c.pk", expectedPageSize: 5, expectedResults: 5, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT TOP 100 c.pk FROM c ORDER BY c.pk", expectedPageSize: 100, expectedResults: 100, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT TOP 5000 c.pk FROM c ORDER BY c.pk", expectedPageSize: 1000, expectedResults: 1100, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT TOP 15000 c.pk FROM c ORDER BY c.pk", expectedPageSize: 1000, expectedResults: 1100, mockInMemoryContainer, documentContainer); - - // TOP without ORDER BY - await this.TestPageSizeAsync("SELECT TOP 5 c.pk FROM c", expectedPageSize: 1000, expectedResults: 5, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT TOP 5000 c.pk FROM c", expectedPageSize: 1000, expectedResults: 1100, mockInMemoryContainer, documentContainer); - await this.TestPageSizeAsync("SELECT TOP 15000 c.pk FROM c", expectedPageSize: 1000, expectedResults: 1100, mockInMemoryContainer, documentContainer); - } - - private async Task TestPageSizeAsync(string query, int expectedPageSize, int expectedResults, MockInMemoryContainer inMemoryContainer, DocumentContainer documentContainer) - { +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Tests.Query.Pipeline +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.IO; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.ChangeFeed.Pagination; + using Microsoft.Azure.Cosmos.CosmosElements; + using Microsoft.Azure.Cosmos.Pagination; + using Microsoft.Azure.Cosmos.Query; + using Microsoft.Azure.Cosmos.Query.Core; + using Microsoft.Azure.Cosmos.Query.Core.ExecutionContext; + using Microsoft.Azure.Cosmos.Query.Core.Monads; + using Microsoft.Azure.Cosmos.Query.Core.Pipeline; + using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination; + using Microsoft.Azure.Cosmos.Query.Core.QueryClient; + using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; + using Microsoft.Azure.Cosmos.ReadFeed.Pagination; + using Microsoft.Azure.Cosmos.Tests.Pagination; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Documents; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + using Newtonsoft.Json; + + [TestClass] + public class FullPipelineTests + { + internal static readonly PartitionKeyDefinition partitionKeyDefinition = new PartitionKeyDefinition() + { + Paths = new Collection() + { + "/pk" + }, + Kind = PartitionKind.Hash, + Version = PartitionKeyDefinitionVersion.V2, + }; + + [TestMethod] + public async Task TestMerge() + { + List documents = Enumerable + .Range(0, 100) + .Select(x => CosmosObject.Parse($"{{\"pk\" : {x} }}")) + .ToList(); + + MergeTestUtil mergeTest = new MergeTestUtil(); + mergeTest.DocumentContainer = await CreateDocumentContainerAsync( + documents: documents, + numSplits: 2, + failureConfigs: new FlakyDocumentContainer.FailureConfigs( + inject429s: false, + injectEmptyPages: false, + shouldReturnFailure: mergeTest.ShouldReturnFailure)); + + string query = "SELECT * FROM c ORDER BY c._ts"; + int pageSize = 10; + IQueryPipelineStage pipelineStage = await CreatePipelineAsync(mergeTest.DocumentContainer, query, pageSize); + + List elements = new List(); + int iteration = 0; + while (await pipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) + { + TryCatch tryGetQueryPage = pipelineStage.Current; + tryGetQueryPage.ThrowIfFailed(); + + elements.AddRange(tryGetQueryPage.Result.Documents); + ++iteration; + + if (iteration == 1) + { + mergeTest.ShouldMerge = MergeTestUtil.TriState.Ready; + } + } + + Assert.AreEqual(expected: documents.Count, actual: elements.Count); + } + + [TestMethod] + public async Task SelectStar() + { + List documents = new List(); + for (int i = 0; i < 250; i++) + { + documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); + } + + List documentsQueried = await ExecuteQueryAsync( + query: "SELECT * FROM c", + documents: documents); + + Assert.AreEqual(expected: documents.Count, actual: documentsQueried.Count); + } + + [TestMethod] + public async Task OrderBy() + { + List documents = new List(); + for (int i = 0; i < 250; i++) + { + documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); + } + + List documentsQueried = await ExecuteQueryAsync( + query: "SELECT * FROM c ORDER BY c._ts", + documents: documents); + + Assert.AreEqual(expected: documents.Count, actual: documentsQueried.Count); + } + + [TestMethod] + [Ignore] // Continuation token for in memory container needs to be updated to suppport this query + public async Task OrderByWithJoins() + { + List documents = new List() + { + CosmosObject.Parse($"{{\"pk\" : {1}, \"children\" : [\"Alice\", \"Bob\", \"Charlie\"]}}"), + CosmosObject.Parse($"{{\"pk\" : {2}, \"children\" : [\"Dave\", \"Eve\", \"Fancy\"]}}"), + CosmosObject.Parse($"{{\"pk\" : {3}, \"children\" : [\"George\", \"Henry\", \"Igor\"]}}"), + CosmosObject.Parse($"{{\"pk\" : {4}, \"children\" : [\"Jack\", \"Kim\", \"Levin\"]}}"), + }; + + List documentsQueried = await ExecuteQueryAsync( + query: "SELECT d FROM c JOIN d in c.children ORDER BY c.pk", + documents: documents, + pageSize: 2); + + Assert.AreEqual(expected: documents.Count * 3, actual: documentsQueried.Count); + } + + [TestMethod] + public async Task Top() + { + List documents = new List(); + for (int i = 0; i < 250; i++) + { + documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); + } + + List documentsQueried = await ExecuteQueryAsync( + query: "SELECT TOP 10 * FROM c", + documents: documents); + + Assert.AreEqual(expected: 10, actual: documentsQueried.Count); + } + + [TestMethod] + public async Task OffsetLimit() + { + List documents = new List(); + for (int i = 0; i < 250; i++) + { + documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); + } + + List documentsQueried = await ExecuteQueryAsync( + query: "SELECT * FROM c OFFSET 10 LIMIT 103", + documents: documents); + + Assert.AreEqual(expected: 103, actual: documentsQueried.Count); + } + + [TestMethod] + public async Task Aggregates() + { + const int DocumentCount = 250; + List documents = new List(); + for (int i = 0; i < DocumentCount; i++) + { + documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); + } + + List documentsQueried = await ExecuteQueryAsync( + query: "SELECT VALUE COUNT(1) FROM c", + documents: documents); + + Assert.AreEqual(expected: 1, actual: documentsQueried.Count); + if (documentsQueried[0] is CosmosNumber number) + { + Assert.AreEqual(expected: DocumentCount, actual: Number64.ToLong(number.Value)); + } + else + { + Assert.Fail(); + } + } + + [TestMethod] + public async Task DCount() + { + List documents = new List(); + for (int i = 0; i < 250; i++) + { + documents.Add(CosmosObject.Parse($"{{\"pk\" : {i}, \"val\": {i % 49} }}")); + } + + List documentsQueried = await ExecuteQueryAsync( + query: "SELECT VALUE COUNT(1) FROM (SELECT DISTINCT VALUE c.val FROM c)", + documents: documents); + + Assert.AreEqual(expected: 1, actual: documentsQueried.Count); + Assert.IsTrue(documentsQueried[0] is CosmosNumber); + CosmosNumber result = documentsQueried[0] as CosmosNumber; + Assert.AreEqual(expected: 49, actual: Number64.ToLong(result.Value)); + } + + [TestMethod] + [Ignore] + // Need to implement group by continuation token on the in memory collection. + public async Task GroupBy() + { + List documents = new List(); + for (int i = 0; i < 250; i++) + { + documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); + } + + List documentsQueried = await ExecuteQueryAsync( + query: "SELECT VALUE COUNT(1) FROM c GROUP BY c.pk", + documents: documents); + + Assert.AreEqual(expected: documents.Count, actual: documentsQueried.Count); + } + + [TestMethod] + public async Task Tracing() + { + List documents = new List(); + for (int i = 0; i < 250; i++) + { + documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); + } + + IDocumentContainer documentContainer = await CreateDocumentContainerAsync(documents); + IQueryPipelineStage pipelineStage = await CreatePipelineAsync(documentContainer, "SELECT * FROM c", pageSize: 10); + + Trace rootTrace; + int numTraces = (await documentContainer.GetFeedRangesAsync(NoOpTrace.Singleton, default)).Count; + using (rootTrace = Trace.GetRootTrace("Cross Partition Query")) + { + while (await pipelineStage.MoveNextAsync(rootTrace, cancellationToken: default)) + { + TryCatch tryGetQueryPage = pipelineStage.Current; + tryGetQueryPage.ThrowIfFailed(); + + numTraces++; + } + } + + rootTrace.SetWalkingStateRecursively(); + Assert.AreEqual(numTraces, rootTrace.Children.Count); + } + + [TestMethod] + public async Task OffsetLimitPageSize() + { + List documents = new List(); + for (int i = 0; i < 1100; i++) + { + documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); + } + + MockInMemoryContainer mockInMemoryContainer = new MockInMemoryContainer(new InMemoryContainer(partitionKeyDefinition)); + DocumentContainer documentContainer = await CreateDocumentContainerAsync(documents, mockInMemoryContainer, numSplits: 4); + + // OFFSET/LIMIT with ORDER BY + await this.TestPageSizeAsync("SELECT c.pk FROM c ORDER BY c.pk OFFSET 0 LIMIT 500", expectedPageSize: 500, expectedResults: 500, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT c.pk FROM c ORDER BY c.pk OFFSET 10000 LIMIT 5000", expectedPageSize: 1000, expectedResults: 0, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT c.pk FROM c ORDER BY c.pk OFFSET 10 LIMIT 100", expectedPageSize: 110, expectedResults: 100, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT c.pk FROM c ORDER BY c.pk OFFSET 0 LIMIT 100", expectedPageSize: 100, expectedResults: 100, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT c.pk FROM c ORDER BY c.pk OFFSET 100 LIMIT 0", expectedPageSize: 100, expectedResults: 0, mockInMemoryContainer, documentContainer); + + // OFFSET/LIMIT without ORDER BY + await this.TestPageSizeAsync("SELECT c.pk FROM c OFFSET 10 LIMIT 100", expectedPageSize: 1000, expectedResults: 100, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT c.pk FROM c OFFSET 0 LIMIT 100", expectedPageSize: 1000, expectedResults: 100, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT c.pk FROM c OFFSET 100 LIMIT 0", expectedPageSize: 1000, expectedResults: 0, mockInMemoryContainer, documentContainer); + + // TOP with ORDER BY + await this.TestPageSizeAsync("SELECT TOP 5 c.pk FROM c ORDER BY c.pk", expectedPageSize: 5, expectedResults: 5, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT TOP 100 c.pk FROM c ORDER BY c.pk", expectedPageSize: 100, expectedResults: 100, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT TOP 5000 c.pk FROM c ORDER BY c.pk", expectedPageSize: 1000, expectedResults: 1100, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT TOP 15000 c.pk FROM c ORDER BY c.pk", expectedPageSize: 1000, expectedResults: 1100, mockInMemoryContainer, documentContainer); + + // TOP without ORDER BY + await this.TestPageSizeAsync("SELECT TOP 5 c.pk FROM c", expectedPageSize: 1000, expectedResults: 5, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT TOP 5000 c.pk FROM c", expectedPageSize: 1000, expectedResults: 1100, mockInMemoryContainer, documentContainer); + await this.TestPageSizeAsync("SELECT TOP 15000 c.pk FROM c", expectedPageSize: 1000, expectedResults: 1100, mockInMemoryContainer, documentContainer); + } + + private async Task TestPageSizeAsync(string query, int expectedPageSize, int expectedResults, MockInMemoryContainer inMemoryContainer, DocumentContainer documentContainer) + { IQueryPipelineStage queryPipelineStage = CreateQueryPipelineStage( - documentContainer, - query, - partitionKeyDefinition, - null, + documentContainer, + query, + partitionKeyDefinition, + null, new QueryRequestOptions()); - - List elements = new List(); - while (await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) - { - TryCatch tryGetQueryPage = queryPipelineStage.Current; - tryGetQueryPage.ThrowIfFailed(); - - elements.AddRange(tryGetQueryPage.Result.Documents); - } - - Assert.AreEqual(expected: expectedPageSize, actual: inMemoryContainer.PageSizeSpecified); - Assert.AreEqual(expected: expectedResults, actual: elements.Count); - } - + + List elements = new List(); + while (await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) + { + TryCatch tryGetQueryPage = queryPipelineStage.Current; + tryGetQueryPage.ThrowIfFailed(); + + elements.AddRange(tryGetQueryPage.Result.Documents); + } + + Assert.AreEqual(expected: expectedPageSize, actual: inMemoryContainer.PageSizeSpecified); + Assert.AreEqual(expected: expectedResults, actual: elements.Count); + } + private static IQueryPipelineStage CreateQueryPipelineStage( - DocumentContainer documentContainer, - string query, - PartitionKeyDefinition partitionKeyDefinition, - Cosmos.PartitionKey? partitionKeyValue, - QueryRequestOptions queryRequestOptions) - { - CosmosSerializerCore serializerCore = new(); - using StreamReader streamReader = new(serializerCore.ToStreamSqlQuerySpec(new SqlQuerySpec(query), Documents.ResourceType.Document)); - string sqlQuerySpecJsonString = streamReader.ReadToEnd(); - - CosmosQueryExecutionContextFactory.InputParameters inputParameters = CosmosQueryExecutionContextFactory.InputParameters.Create( - sqlQuerySpec: new SqlQuerySpec(query), - initialUserContinuationToken: null, - initialFeedRange: null, - maxConcurrency: queryRequestOptions.MaxConcurrency, - maxItemCount: queryRequestOptions.MaxItemCount, - maxBufferedItemCount: queryRequestOptions.MaxBufferedItemCount, - partitionKey: partitionKeyValue, - properties: new Dictionary() { { "x-ms-query-partitionkey-definition", partitionKeyDefinition } }, - partitionedQueryExecutionInfo: null, - returnResultsInDeterministicOrder: null, + DocumentContainer documentContainer, + string query, + PartitionKeyDefinition partitionKeyDefinition, + Cosmos.PartitionKey? partitionKeyValue, + QueryRequestOptions queryRequestOptions) + { + CosmosSerializerCore serializerCore = new(); + using StreamReader streamReader = new(serializerCore.ToStreamSqlQuerySpec(new SqlQuerySpec(query), Documents.ResourceType.Document)); + string sqlQuerySpecJsonString = streamReader.ReadToEnd(); + + CosmosQueryExecutionContextFactory.InputParameters inputParameters = CosmosQueryExecutionContextFactory.InputParameters.Create( + sqlQuerySpec: new SqlQuerySpec(query), + initialUserContinuationToken: null, + initialFeedRange: null, + maxConcurrency: queryRequestOptions.MaxConcurrency, + maxItemCount: queryRequestOptions.MaxItemCount, + maxBufferedItemCount: queryRequestOptions.MaxBufferedItemCount, + partitionKey: partitionKeyValue, + properties: new Dictionary() { { "x-ms-query-partitionkey-definition", partitionKeyDefinition } }, + partitionedQueryExecutionInfo: null, + returnResultsInDeterministicOrder: null, enableOptimisticDirectExecution: queryRequestOptions.EnableOptimisticDirectExecution, isHybridSearchQueryPlanOptimizationDisabled: queryRequestOptions.IsHybridSearchQueryPlanOptimizationDisabled, - enableDistributedQueryGatewayMode: queryRequestOptions.EnableDistributedQueryGatewayMode, - testInjections: queryRequestOptions.TestSettings); - - string databaseId = "db1234"; - string resourceLink = $"dbs/{databaseId}/colls"; - const string suffix = "-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF"; - - List partitionKeyRanges = new List - { - new PartitionKeyRange() { MinInclusive = Documents.Routing.PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, MaxExclusive = "1F" + suffix }, - new PartitionKeyRange() { MinInclusive = "1F" + suffix, MaxExclusive = "3F" + suffix }, - new PartitionKeyRange() { MinInclusive = "3F" + suffix, MaxExclusive = "5F" + suffix }, - new PartitionKeyRange() { MinInclusive = "5F" + suffix, MaxExclusive = "7F" + suffix }, - new PartitionKeyRange() { MinInclusive = "7F" + suffix, MaxExclusive = "9F" + suffix }, - new PartitionKeyRange() { MinInclusive = "9F" + suffix, MaxExclusive = "BF" + suffix }, - new PartitionKeyRange() { MinInclusive = "BF" + suffix, MaxExclusive = "DF" + suffix }, - new PartitionKeyRange() { MinInclusive = "DF" + suffix, MaxExclusive = Documents.Routing.PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey }, - }; - - Mock mockClient = new Mock(); - - mockClient.Setup(x => x.GetTargetPartitionKeyRangesAsync( - It.IsAny(), - "HelloWorld", - It.IsAny>>(), - It.IsAny(), - It.IsAny())) - .Returns((string resourceLink, string collectionResourceId, IReadOnlyList> providedRanges, bool forceRefresh, ITrace trace) => Task.FromResult(partitionKeyRanges)); - - mockClient.Setup(x => x.TryGetPartitionedQueryExecutionInfoAsync( - It.IsAny(), - It.IsAny(), + enableDistributedQueryGatewayMode: queryRequestOptions.EnableDistributedQueryGatewayMode, + fullTextScoreScope: queryRequestOptions.FullTextScoreScope, + testInjections: queryRequestOptions.TestSettings); + + string databaseId = "db1234"; + string resourceLink = $"dbs/{databaseId}/colls"; + const string suffix = "-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF-FF"; + + List partitionKeyRanges = new List + { + new PartitionKeyRange() { MinInclusive = Documents.Routing.PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, MaxExclusive = "1F" + suffix }, + new PartitionKeyRange() { MinInclusive = "1F" + suffix, MaxExclusive = "3F" + suffix }, + new PartitionKeyRange() { MinInclusive = "3F" + suffix, MaxExclusive = "5F" + suffix }, + new PartitionKeyRange() { MinInclusive = "5F" + suffix, MaxExclusive = "7F" + suffix }, + new PartitionKeyRange() { MinInclusive = "7F" + suffix, MaxExclusive = "9F" + suffix }, + new PartitionKeyRange() { MinInclusive = "9F" + suffix, MaxExclusive = "BF" + suffix }, + new PartitionKeyRange() { MinInclusive = "BF" + suffix, MaxExclusive = "DF" + suffix }, + new PartitionKeyRange() { MinInclusive = "DF" + suffix, MaxExclusive = Documents.Routing.PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey }, + }; + + Mock mockClient = new Mock(); + + mockClient.Setup(x => x.GetTargetPartitionKeyRangesAsync( + It.IsAny(), + "HelloWorld", + It.IsAny>>(), + It.IsAny(), + It.IsAny())) + .Returns((string resourceLink, string collectionResourceId, IReadOnlyList> providedRanges, bool forceRefresh, ITrace trace) => Task.FromResult(partitionKeyRanges)); + + mockClient.Setup(x => x.TryGetPartitionedQueryExecutionInfoAsync( + It.IsAny(), + It.IsAny(), It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) .Returns(( SqlQuerySpec sqlQuerySpec, ResourceType resourceType, @@ -393,364 +395,365 @@ private static IQueryPipelineStage CreateQueryPipelineStage( bool hasLogicalPartitionKey, bool allowDCount, bool useSystemPrefix, - bool isHybridSearchQueryPlanOptimizationDisabled, + bool isHybridSearchQueryPlanOptimizationDisabled, Cosmos.GeospatialType geospatialType, - CancellationToken cancellationToken) => - { - CosmosSerializerCore serializerCore = new(); - using StreamReader streamReader = new(serializerCore.ToStreamSqlQuerySpec(sqlQuerySpec, Documents.ResourceType.Document)); - string sqlQuerySpecJsonString = streamReader.ReadToEnd(); - - (PartitionedQueryExecutionInfo partitionedQueryExecutionInfo, QueryPartitionProvider queryPartitionProvider) = OptimisticDirectExecutionQueryBaselineTests.GetPartitionedQueryExecutionInfoAndPartitionProvider(sqlQuerySpecJsonString, partitionKeyDefinition); - return Task.FromResult(TryCatch.FromResult(partitionedQueryExecutionInfo)); - } - ); - - List targetPartitionKeyRanges = new (){ - new PartitionKeyRange() - { - MinInclusive = "", - MaxExclusive = "FF" - } - }; - CosmosQueryContextCore cosmosQueryContextCore = new CosmosQueryContextCore( - client: new TestCosmosQueryClient(GetQueryPartitionProvider(), targetPartitionKeyRanges), - resourceTypeEnum: Documents.ResourceType.Document, - operationType: Documents.OperationType.Query, - resourceType: typeof(QueryResponseCore), - resourceLink: resourceLink, - isContinuationExpected: true, - allowNonValueAggregateQuery: true, - useSystemPrefix: false, + CancellationToken cancellationToken) => + { + CosmosSerializerCore serializerCore = new(); + using StreamReader streamReader = new(serializerCore.ToStreamSqlQuerySpec(sqlQuerySpec, Documents.ResourceType.Document)); + string sqlQuerySpecJsonString = streamReader.ReadToEnd(); + + (PartitionedQueryExecutionInfo partitionedQueryExecutionInfo, QueryPartitionProvider queryPartitionProvider) = OptimisticDirectExecutionQueryBaselineTests.GetPartitionedQueryExecutionInfoAndPartitionProvider(sqlQuerySpecJsonString, partitionKeyDefinition); + return Task.FromResult(TryCatch.FromResult(partitionedQueryExecutionInfo)); + } + ); + + List targetPartitionKeyRanges = new (){ + new PartitionKeyRange() + { + MinInclusive = "", + MaxExclusive = "FF" + } + }; + CosmosQueryContextCore cosmosQueryContextCore = new CosmosQueryContextCore( + client: new TestCosmosQueryClient(GetQueryPartitionProvider(), targetPartitionKeyRanges), + resourceTypeEnum: Documents.ResourceType.Document, + operationType: Documents.OperationType.Query, + resourceType: typeof(QueryResponseCore), + resourceLink: resourceLink, + isContinuationExpected: true, + allowNonValueAggregateQuery: true, + useSystemPrefix: false, correlatedActivityId: Guid.NewGuid()); - IQueryPipelineStage queryPipelineStage = CosmosQueryExecutionContextFactory.Create( - documentContainer, + IQueryPipelineStage queryPipelineStage = CosmosQueryExecutionContextFactory.Create( + documentContainer, cosmosQueryContextCore, - inputParameters, - NoOpTrace.Singleton); - - return queryPipelineStage; - } - - internal static QueryPartitionProvider GetQueryPartitionProvider() - { - IDictionary DefaultQueryengineConfiguration = new Dictionary() - { - {"maxSqlQueryInputLength", 30720}, - {"maxJoinsPerSqlQuery", 5}, - {"maxLogicalAndPerSqlQuery", 200}, - {"maxLogicalOrPerSqlQuery", 200}, - {"maxUdfRefPerSqlQuery", 2}, - {"maxInExpressionItemsCount", 8000}, - {"queryMaxInMemorySortDocumentCount", 500}, - {"maxQueryRequestTimeoutFraction", 0.90}, - {"sqlAllowNonFiniteNumbers", false}, - {"sqlAllowAggregateFunctions", true}, - {"sqlAllowSubQuery", true}, - {"sqlAllowScalarSubQuery", false}, - {"allowNewKeywords", true}, - {"sqlAllowLike", true}, - {"sqlAllowGroupByClause", false}, - {"queryEnableMongoNativeRegex", true}, - {"maxSpatialQueryCells", 12}, - {"spatialMaxGeometryPointCount", 256}, - {"sqlDisableOptimizationFlags", 0}, - {"sqlEnableParameterExpansionCheck", true} - }; - - return new QueryPartitionProvider(DefaultQueryengineConfiguration); - } - - internal static async Task> ExecuteQueryAsync( - string query, - IReadOnlyList documents, - int pageSize = 10) - { - IDocumentContainer documentContainer = await CreateDocumentContainerAsync(documents); - return await ExecuteQueryAsync(query, documentContainer, pageSize); - } - - internal static async Task> ExecuteQueryAsync( - string query, - IDocumentContainer documentContainer, - int pageSize) - { - List resultsFromDrainWithoutState = await DrainWithoutStateAsync(query, documentContainer, pageSize); - List resultsFromDrainWithState = await DrainWithStateAsync(query, documentContainer, pageSize); - - Assert.IsTrue(resultsFromDrainWithoutState.SequenceEqual(resultsFromDrainWithState)); - - return resultsFromDrainWithoutState; - } - - [TestMethod] - public async Task Fuzz() - { - List documents = new List(); - for (int i = 0; i < 250; i++) - { - documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); - } - - List documentsQueried = await ExecuteQueryAsync( - query: "SELECT * FROM c ORDER BY c._ts OFFSET 1 LIMIT 500", - documents: documents); - - Assert.AreEqual(expected: 249, actual: documentsQueried.Count); - } - - internal static async Task> DrainWithoutStateAsync(string query, IDocumentContainer documentContainer, int pageSize) - { - IQueryPipelineStage pipelineStage = await CreatePipelineAsync(documentContainer, query, pageSize); - - List elements = new List(); - while (await pipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) - { - TryCatch tryGetQueryPage = pipelineStage.Current; - tryGetQueryPage.ThrowIfFailed(); - - elements.AddRange(tryGetQueryPage.Result.Documents); - } - - return elements; - } - - private static async Task> DrainWithStateAsync(string query, IDocumentContainer documentContainer, int pageSize) - { - IQueryPipelineStage pipelineStage; - CosmosElement state = null; - - List elements = new List(); - do - { - pipelineStage = await CreatePipelineAsync(documentContainer, query, pageSize, state); - - if (!await pipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) - { - break; - } - - TryCatch tryGetQueryPage = pipelineStage.Current; - tryGetQueryPage.ThrowIfFailed(); - - elements.AddRange(tryGetQueryPage.Result.Documents); - state = tryGetQueryPage.Result.State?.Value; - } - while (state != null); - - return elements; - } - - internal static Task CreateDocumentContainerAsync( - IReadOnlyList documents, - int numSplits = 3, - FlakyDocumentContainer.FailureConfigs failureConfigs = null) - { - IMonadicDocumentContainer monadicDocumentContainer = CreateMonadicDocumentContainerAsync(failureConfigs); - return CreateDocumentContainerAsync(documents, monadicDocumentContainer, numSplits); - } - - internal static IMonadicDocumentContainer CreateMonadicDocumentContainerAsync(FlakyDocumentContainer.FailureConfigs failureConfigs) - { - IMonadicDocumentContainer monadicDocumentContainer = new InMemoryContainer(partitionKeyDefinition); - if (failureConfigs != null) - { - monadicDocumentContainer = new FlakyDocumentContainer(monadicDocumentContainer, failureConfigs); - } - - return monadicDocumentContainer; - } - - internal static async Task CreateDocumentContainerAsync( - IReadOnlyList documents, - IMonadicDocumentContainer monadicDocumentContainer, - int numSplits) - { - DocumentContainer documentContainer = new DocumentContainer(monadicDocumentContainer); - - for (int i = 0; i < numSplits; i++) - { - IReadOnlyList ranges = await documentContainer.GetFeedRangesAsync( - trace: NoOpTrace.Singleton, - cancellationToken: default); - foreach (FeedRangeInternal range in ranges) - { - await documentContainer.SplitAsync(range, cancellationToken: default); - } - - await documentContainer.RefreshProviderAsync(NoOpTrace.Singleton, cancellationToken: default); - } - - foreach (CosmosObject document in documents) - { - while (true) - { - TryCatch monadicCreateRecord = await documentContainer.MonadicCreateItemAsync( - document, - cancellationToken: default); - if (monadicCreateRecord.Succeeded) - { - break; - } - } - } - - return documentContainer; - } - - private static async Task CreatePipelineAsync( - IDocumentContainer documentContainer, - string query, - int pageSize, - CosmosElement state = null) - { - IReadOnlyList feedRanges = await documentContainer.GetFeedRangesAsync(NoOpTrace.Singleton, cancellationToken: default); - - TryCatch tryCreatePipeline = PipelineFactory.MonadicCreate( - documentContainer, - new SqlQuerySpec(query), - feedRanges, - partitionKey: null, + inputParameters, + NoOpTrace.Singleton); + + return queryPipelineStage; + } + + internal static QueryPartitionProvider GetQueryPartitionProvider() + { + IDictionary DefaultQueryengineConfiguration = new Dictionary() + { + {"maxSqlQueryInputLength", 30720}, + {"maxJoinsPerSqlQuery", 5}, + {"maxLogicalAndPerSqlQuery", 200}, + {"maxLogicalOrPerSqlQuery", 200}, + {"maxUdfRefPerSqlQuery", 2}, + {"maxInExpressionItemsCount", 8000}, + {"queryMaxInMemorySortDocumentCount", 500}, + {"maxQueryRequestTimeoutFraction", 0.90}, + {"sqlAllowNonFiniteNumbers", false}, + {"sqlAllowAggregateFunctions", true}, + {"sqlAllowSubQuery", true}, + {"sqlAllowScalarSubQuery", false}, + {"allowNewKeywords", true}, + {"sqlAllowLike", true}, + {"sqlAllowGroupByClause", false}, + {"queryEnableMongoNativeRegex", true}, + {"maxSpatialQueryCells", 12}, + {"spatialMaxGeometryPointCount", 256}, + {"sqlDisableOptimizationFlags", 0}, + {"sqlEnableParameterExpansionCheck", true} + }; + + return new QueryPartitionProvider(DefaultQueryengineConfiguration); + } + + internal static async Task> ExecuteQueryAsync( + string query, + IReadOnlyList documents, + int pageSize = 10) + { + IDocumentContainer documentContainer = await CreateDocumentContainerAsync(documents); + return await ExecuteQueryAsync(query, documentContainer, pageSize); + } + + internal static async Task> ExecuteQueryAsync( + string query, + IDocumentContainer documentContainer, + int pageSize) + { + List resultsFromDrainWithoutState = await DrainWithoutStateAsync(query, documentContainer, pageSize); + List resultsFromDrainWithState = await DrainWithStateAsync(query, documentContainer, pageSize); + + Assert.IsTrue(resultsFromDrainWithoutState.SequenceEqual(resultsFromDrainWithState)); + + return resultsFromDrainWithoutState; + } + + [TestMethod] + public async Task Fuzz() + { + List documents = new List(); + for (int i = 0; i < 250; i++) + { + documents.Add(CosmosObject.Parse($"{{\"pk\" : {i} }}")); + } + + List documentsQueried = await ExecuteQueryAsync( + query: "SELECT * FROM c ORDER BY c._ts OFFSET 1 LIMIT 500", + documents: documents); + + Assert.AreEqual(expected: 249, actual: documentsQueried.Count); + } + + internal static async Task> DrainWithoutStateAsync(string query, IDocumentContainer documentContainer, int pageSize) + { + IQueryPipelineStage pipelineStage = await CreatePipelineAsync(documentContainer, query, pageSize); + + List elements = new List(); + while (await pipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) + { + TryCatch tryGetQueryPage = pipelineStage.Current; + tryGetQueryPage.ThrowIfFailed(); + + elements.AddRange(tryGetQueryPage.Result.Documents); + } + + return elements; + } + + private static async Task> DrainWithStateAsync(string query, IDocumentContainer documentContainer, int pageSize) + { + IQueryPipelineStage pipelineStage; + CosmosElement state = null; + + List elements = new List(); + do + { + pipelineStage = await CreatePipelineAsync(documentContainer, query, pageSize, state); + + if (!await pipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) + { + break; + } + + TryCatch tryGetQueryPage = pipelineStage.Current; + tryGetQueryPage.ThrowIfFailed(); + + elements.AddRange(tryGetQueryPage.Result.Documents); + state = tryGetQueryPage.Result.State?.Value; + } + while (state != null); + + return elements; + } + + internal static Task CreateDocumentContainerAsync( + IReadOnlyList documents, + int numSplits = 3, + FlakyDocumentContainer.FailureConfigs failureConfigs = null) + { + IMonadicDocumentContainer monadicDocumentContainer = CreateMonadicDocumentContainerAsync(failureConfigs); + return CreateDocumentContainerAsync(documents, monadicDocumentContainer, numSplits); + } + + internal static IMonadicDocumentContainer CreateMonadicDocumentContainerAsync(FlakyDocumentContainer.FailureConfigs failureConfigs) + { + IMonadicDocumentContainer monadicDocumentContainer = new InMemoryContainer(partitionKeyDefinition); + if (failureConfigs != null) + { + monadicDocumentContainer = new FlakyDocumentContainer(monadicDocumentContainer, failureConfigs); + } + + return monadicDocumentContainer; + } + + internal static async Task CreateDocumentContainerAsync( + IReadOnlyList documents, + IMonadicDocumentContainer monadicDocumentContainer, + int numSplits) + { + DocumentContainer documentContainer = new DocumentContainer(monadicDocumentContainer); + + for (int i = 0; i < numSplits; i++) + { + IReadOnlyList ranges = await documentContainer.GetFeedRangesAsync( + trace: NoOpTrace.Singleton, + cancellationToken: default); + foreach (FeedRangeInternal range in ranges) + { + await documentContainer.SplitAsync(range, cancellationToken: default); + } + + await documentContainer.RefreshProviderAsync(NoOpTrace.Singleton, cancellationToken: default); + } + + foreach (CosmosObject document in documents) + { + while (true) + { + TryCatch monadicCreateRecord = await documentContainer.MonadicCreateItemAsync( + document, + cancellationToken: default); + if (monadicCreateRecord.Succeeded) + { + break; + } + } + } + + return documentContainer; + } + + private static async Task CreatePipelineAsync( + IDocumentContainer documentContainer, + string query, + int pageSize, + CosmosElement state = null) + { + IReadOnlyList feedRanges = await documentContainer.GetFeedRangesAsync(NoOpTrace.Singleton, cancellationToken: default); + + TryCatch tryCreatePipeline = PipelineFactory.MonadicCreate( + documentContainer, + new SqlQuerySpec(query), + feedRanges, + partitionKey: null, GetQueryPlan(query), hybridSearchQueryInfo: null, - allRanges: feedRanges, - maxItemCount: pageSize, + allRanges: feedRanges, + maxItemCount: pageSize, containerQueryProperties: new ContainerQueryProperties(), - isContinuationExpected: true, - maxConcurrency: 10, - requestContinuationToken: state); - - tryCreatePipeline.ThrowIfFailed(); - - return tryCreatePipeline.Result; - } - - private static QueryInfo GetQueryPlan(string query) - { - TryCatch info = QueryPartitionProviderTestInstance.Object.TryGetPartitionedQueryExecutionInfoInternal( - JsonConvert.SerializeObject(new SqlQuerySpec(query)), + isContinuationExpected: true, + maxConcurrency: 10, + fullTextScoreScope: FullTextScoreScope.Global, + requestContinuationToken: state); + + tryCreatePipeline.ThrowIfFailed(); + + return tryCreatePipeline.Result; + } + + private static QueryInfo GetQueryPlan(string query) + { + TryCatch info = QueryPartitionProviderTestInstance.Object.TryGetPartitionedQueryExecutionInfoInternal( + JsonConvert.SerializeObject(new SqlQuerySpec(query)), partitionKeyDefinition, - vectorEmbeddingPolicy: null, - requireFormattableOrderByQuery: true, - isContinuationExpected: false, - allowNonValueAggregateQuery: true, - allowDCount: true, + vectorEmbeddingPolicy: null, + requireFormattableOrderByQuery: true, + isContinuationExpected: false, + allowNonValueAggregateQuery: true, + allowDCount: true, hasLogicalPartitionKey: false, - hybridSearchSkipOrderByRewrite: false, - useSystemPrefix: false, - geospatialType: Cosmos.GeospatialType.Geography); - - info.ThrowIfFailed(); - return info.Result.QueryInfo; - } - - private class MergeTestUtil - { - public enum TriState { NotReady, Ready, Done }; - - public IDocumentContainer DocumentContainer { get; set; } - - public TriState ShouldMerge { get; set; } - - public async Task ShouldReturnFailure() - { - if (this.ShouldMerge == TriState.Ready) - { - await this.DocumentContainer.RefreshProviderAsync(NoOpTrace.Singleton, cancellationToken: default); - List ranges = await this.DocumentContainer.GetFeedRangesAsync( - trace: NoOpTrace.Singleton, - cancellationToken: default); - - await this.DocumentContainer.MergeAsync(ranges[0], ranges[1], default); - this.ShouldMerge = TriState.Done; - - return new CosmosException( - message: "PKRange was split/merged", - statusCode: System.Net.HttpStatusCode.Gone, - subStatusCode: (int)Documents.SubStatusCodes.PartitionKeyRangeGone, - activityId: "BC0CCDA5-D378-4922-B8B0-D51D745B9139", - requestCharge: 0.0); - } - else - { - return null; - } - } - } - - private class MockInMemoryContainer : IMonadicDocumentContainer - { - public int? PageSizeSpecified { get; private set; } - public IMonadicDocumentContainer MonadicDocumentContainer { get; } - - public MockInMemoryContainer(IMonadicDocumentContainer documentContainer) - { - this.MonadicDocumentContainer = documentContainer; - this.PageSizeSpecified = null; - } - - public void Reset() - { - this.PageSizeSpecified = null; - } - - public Task> MonadicChangeFeedAsync(FeedRangeState feedRangeState, ChangeFeedExecutionOptions changeFeedPaginationOptions, ITrace trace, CancellationToken cancellationToken) - { - return this.MonadicDocumentContainer.MonadicChangeFeedAsync(feedRangeState, changeFeedPaginationOptions, trace, cancellationToken); - } - - public Task> MonadicCreateItemAsync(CosmosObject payload, CancellationToken cancellationToken) - { - return this.MonadicDocumentContainer.MonadicCreateItemAsync(payload, cancellationToken); - } - - public Task>> MonadicGetChildRangeAsync(FeedRangeInternal feedRange, ITrace trace, CancellationToken cancellationToken) - { - return this.MonadicDocumentContainer.MonadicGetChildRangeAsync(feedRange, trace, cancellationToken); - } - - public Task>> MonadicGetFeedRangesAsync(ITrace trace, CancellationToken cancellationToken) - { - return this.MonadicDocumentContainer.MonadicGetFeedRangesAsync(trace, cancellationToken); - } - - public Task> MonadicGetResourceIdentifierAsync(ITrace trace, CancellationToken cancellationToken) - { - return this.MonadicDocumentContainer.MonadicGetResourceIdentifierAsync(trace, cancellationToken); - } - - public Task MonadicMergeAsync(FeedRangeInternal feedRange1, FeedRangeInternal feedRange2, CancellationToken cancellationToken) - { - return this.MonadicDocumentContainer.MonadicMergeAsync(feedRange1, feedRange2, cancellationToken); - } - - public Task> MonadicQueryAsync(SqlQuerySpec sqlQuerySpec, FeedRangeState feedRangeState, QueryExecutionOptions queryPaginationOptions, ITrace trace, CancellationToken cancellationToken) - { - this.PageSizeSpecified = queryPaginationOptions.PageSizeLimit; - - return this.MonadicDocumentContainer.MonadicQueryAsync(sqlQuerySpec, feedRangeState, queryPaginationOptions, trace, cancellationToken); - } - - public Task> MonadicReadFeedAsync(FeedRangeState feedRangeState, ReadFeedExecutionOptions readFeedPaginationOptions, ITrace trace, CancellationToken cancellationToken) - { - return this.MonadicDocumentContainer.MonadicReadFeedAsync(feedRangeState, readFeedPaginationOptions, trace, cancellationToken); - } - - public Task> MonadicReadItemAsync(CosmosElement partitionKey, string identifier, CancellationToken cancellationToken) - { - return this.MonadicDocumentContainer.MonadicReadItemAsync(partitionKey, identifier, cancellationToken); - } - - public Task MonadicRefreshProviderAsync(ITrace trace, CancellationToken cancellationToken) - { - return this.MonadicDocumentContainer.MonadicRefreshProviderAsync(trace, cancellationToken); - } - - public Task MonadicSplitAsync(FeedRangeInternal feedRange, CancellationToken cancellationToken) - { - return this.MonadicDocumentContainer.MonadicSplitAsync(feedRange, cancellationToken); - } - } - } -} + hybridSearchSkipOrderByRewrite: false, + useSystemPrefix: false, + geospatialType: Cosmos.GeospatialType.Geography); + + info.ThrowIfFailed(); + return info.Result.QueryInfo; + } + + private class MergeTestUtil + { + public enum TriState { NotReady, Ready, Done }; + + public IDocumentContainer DocumentContainer { get; set; } + + public TriState ShouldMerge { get; set; } + + public async Task ShouldReturnFailure() + { + if (this.ShouldMerge == TriState.Ready) + { + await this.DocumentContainer.RefreshProviderAsync(NoOpTrace.Singleton, cancellationToken: default); + List ranges = await this.DocumentContainer.GetFeedRangesAsync( + trace: NoOpTrace.Singleton, + cancellationToken: default); + + await this.DocumentContainer.MergeAsync(ranges[0], ranges[1], default); + this.ShouldMerge = TriState.Done; + + return new CosmosException( + message: "PKRange was split/merged", + statusCode: System.Net.HttpStatusCode.Gone, + subStatusCode: (int)Documents.SubStatusCodes.PartitionKeyRangeGone, + activityId: "BC0CCDA5-D378-4922-B8B0-D51D745B9139", + requestCharge: 0.0); + } + else + { + return null; + } + } + } + + private class MockInMemoryContainer : IMonadicDocumentContainer + { + public int? PageSizeSpecified { get; private set; } + public IMonadicDocumentContainer MonadicDocumentContainer { get; } + + public MockInMemoryContainer(IMonadicDocumentContainer documentContainer) + { + this.MonadicDocumentContainer = documentContainer; + this.PageSizeSpecified = null; + } + + public void Reset() + { + this.PageSizeSpecified = null; + } + + public Task> MonadicChangeFeedAsync(FeedRangeState feedRangeState, ChangeFeedExecutionOptions changeFeedPaginationOptions, ITrace trace, CancellationToken cancellationToken) + { + return this.MonadicDocumentContainer.MonadicChangeFeedAsync(feedRangeState, changeFeedPaginationOptions, trace, cancellationToken); + } + + public Task> MonadicCreateItemAsync(CosmosObject payload, CancellationToken cancellationToken) + { + return this.MonadicDocumentContainer.MonadicCreateItemAsync(payload, cancellationToken); + } + + public Task>> MonadicGetChildRangeAsync(FeedRangeInternal feedRange, ITrace trace, CancellationToken cancellationToken) + { + return this.MonadicDocumentContainer.MonadicGetChildRangeAsync(feedRange, trace, cancellationToken); + } + + public Task>> MonadicGetFeedRangesAsync(ITrace trace, CancellationToken cancellationToken) + { + return this.MonadicDocumentContainer.MonadicGetFeedRangesAsync(trace, cancellationToken); + } + + public Task> MonadicGetResourceIdentifierAsync(ITrace trace, CancellationToken cancellationToken) + { + return this.MonadicDocumentContainer.MonadicGetResourceIdentifierAsync(trace, cancellationToken); + } + + public Task MonadicMergeAsync(FeedRangeInternal feedRange1, FeedRangeInternal feedRange2, CancellationToken cancellationToken) + { + return this.MonadicDocumentContainer.MonadicMergeAsync(feedRange1, feedRange2, cancellationToken); + } + + public Task> MonadicQueryAsync(SqlQuerySpec sqlQuerySpec, FeedRangeState feedRangeState, QueryExecutionOptions queryPaginationOptions, ITrace trace, CancellationToken cancellationToken) + { + this.PageSizeSpecified = queryPaginationOptions.PageSizeLimit; + + return this.MonadicDocumentContainer.MonadicQueryAsync(sqlQuerySpec, feedRangeState, queryPaginationOptions, trace, cancellationToken); + } + + public Task> MonadicReadFeedAsync(FeedRangeState feedRangeState, ReadFeedExecutionOptions readFeedPaginationOptions, ITrace trace, CancellationToken cancellationToken) + { + return this.MonadicDocumentContainer.MonadicReadFeedAsync(feedRangeState, readFeedPaginationOptions, trace, cancellationToken); + } + + public Task> MonadicReadItemAsync(CosmosElement partitionKey, string identifier, CancellationToken cancellationToken) + { + return this.MonadicDocumentContainer.MonadicReadItemAsync(partitionKey, identifier, cancellationToken); + } + + public Task MonadicRefreshProviderAsync(ITrace trace, CancellationToken cancellationToken) + { + return this.MonadicDocumentContainer.MonadicRefreshProviderAsync(trace, cancellationToken); + } + + public Task MonadicSplitAsync(FeedRangeInternal feedRange, CancellationToken cancellationToken) + { + return this.MonadicDocumentContainer.MonadicSplitAsync(feedRange, cancellationToken); + } + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/NonStreamingOrderByQueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/NonStreamingOrderByQueryTests.cs index f9645ef2a5..15ab2cbd40 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/NonStreamingOrderByQueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/NonStreamingOrderByQueryTests.cs @@ -258,6 +258,14 @@ FROM c await RunParityTests(testCases); } + [TestMethod] + public void HybridSearchDefaultScopeIsGlobalTests() + { + // Verify that default QueryRequestOptions.FullTextScoreScope is Global + QueryRequestOptions options = new QueryRequestOptions(); + Assert.AreEqual(FullTextScoreScope.Global, options.FullTextScoreScope); + } + [TestMethod] public async Task HybridSearchTests() { @@ -313,6 +321,87 @@ public async Task HybridSearchTests() take: 10, pageSize: 10, returnEmptyGlobalStatistics: true), + + // Local scope tests - query subset of ranges, statistics also from subset + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: null, + take: 100, + pageSize: 1000, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 2), + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: 10, + take: 50, + pageSize: 100, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 3), + // Local scope with single target range + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: null, + take: 50, + pageSize: 100, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 1), + // Local scope with larger page size than document count + MakeHybridSearchTest( + leafPageCount: 2, + backendPageSize: 5, + requiresGlobalStatistics: true, + skip: null, + take: 100, + pageSize: 1000, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 2), + // Local scope with skip and take + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: 5, + take: 20, + pageSize: 10, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 2), + // Local scope with small page size (multiple pages) + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: null, + take: 50, + pageSize: 5, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 3), + // Local scope with 4 target ranges (majority of 6) + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: 10, + take: 100, + pageSize: 50, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 4), + + // Global scope test - query subset of ranges, statistics from ALL ranges + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: null, + take: 100, + pageSize: 1000, + fullTextScoreScope: FullTextScoreScope.Global, + targetRangeCount: 2), }; foreach (HybridSearchTest testCase in testCases) @@ -376,6 +465,57 @@ public async Task HybridSearchSkipOrderByRewriteTests() take: 10, pageSize: 10, returnEmptyGlobalStatistics: true), + + // Local scope test with skip order by rewrite + MakeHybridSearchSkipOrderByRewriteTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: 20, + take: 100, + pageSize: 1000, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 2), + // Local scope with single target range + MakeHybridSearchSkipOrderByRewriteTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: null, + take: 50, + pageSize: 100, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 1), + // Local scope with small page size + MakeHybridSearchSkipOrderByRewriteTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: 5, + take: 30, + pageSize: 5, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 3), + // Local scope with different skip/take values + MakeHybridSearchSkipOrderByRewriteTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: 10, + take: 25, + pageSize: 10, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 2), + // Local scope with 4 target ranges + MakeHybridSearchSkipOrderByRewriteTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: null, + take: 100, + pageSize: 50, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 4), }; foreach (HybridSearchTest testCase in testCases) @@ -439,6 +579,62 @@ public async Task HybridSearchWeightedRRFTests() weights: new double[] { -1.25, -2.0 }, pageSize: 10, returnEmptyGlobalStatistics: true), + + // Local scope test with weighted RRF + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: null, + take: 100, + weights: new double[] { 1.0, 1.0 }, + pageSize: 1000, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 2), + // Local scope with different weights + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: 10, + take: 50, + weights: new double[] { 0.5, 1.5 }, + pageSize: 100, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 3), + // Local scope with single target range and weights + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: null, + take: 50, + weights: new double[] { 2.0, 1.0 }, + pageSize: 50, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 1), + // Local scope with small page size and weights + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: 5, + take: 30, + weights: new double[] { 1.0, 2.0 }, + pageSize: 5, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 2), + // Local scope with 4 target ranges and weights + MakeHybridSearchTest( + leafPageCount: 4, + backendPageSize: 10, + requiresGlobalStatistics: true, + skip: null, + take: 100, + weights: new double[] { 0.75, 1.25 }, + pageSize: 25, + fullTextScoreScope: FullTextScoreScope.Local, + targetRangeCount: 4), }; foreach (HybridSearchTest testCase in testCases) @@ -512,7 +708,7 @@ public async Task HybridSearchSkipOrderByRewriteWeightedRRFTests() private static async Task RunHybridSearchTest(HybridSearchTest testCase) { - IReadOnlyList ranges = new List + IReadOnlyList allRanges = new List { new FeedRangeEpk(new Documents.Routing.Range(string.Empty, "AA", true, false)), new FeedRangeEpk(new Documents.Routing.Range("AA", "BB", true, false)), @@ -522,12 +718,36 @@ private static async Task RunHybridSearchTest(HybridSearchTest testCase) new FeedRangeEpk(new Documents.Routing.Range("EE", "FF", true, false)), }; - int feedRangeCount = ranges.Count; - int documentCount = feedRangeCount * testCase.LeafPageCount * testCase.BackendPageSize; - - IEnumerable expectedIndices = Enumerable - .Range(0, documentCount) - .Reverse(); + // Determine target ranges based on test case + IReadOnlyList targetRanges = testCase.TargetRangeCount.HasValue + ? allRanges.Take(testCase.TargetRangeCount.Value).ToList() + : allRanges; + + int targetRangeCount = targetRanges.Count; + int allRangeCount = allRanges.Count; + + // Calculate expected indices based on target ranges + // Documents are distributed across ALL ranges with interleaved indices: + // Range 0: indices 0, 6, 12, 18, ... + // Range 1: indices 1, 7, 13, 19, ... + // etc. + IEnumerable expectedIndices; + if (testCase.TargetRangeCount.HasValue && testCase.TargetRangeCount.Value < allRangeCount) + { + // Calculate expected indices for subset of ranges + int docsPerRange = testCase.LeafPageCount * testCase.BackendPageSize; + expectedIndices = Enumerable.Range(0, docsPerRange) + .SelectMany(docInRange => + Enumerable.Range(0, testCase.TargetRangeCount.Value) + .Select(rangeIndex => rangeIndex + (docInRange * allRangeCount))) + .OrderByDescending(x => x); + } + else + { + // Original calculation for all ranges + int documentCount = allRangeCount * testCase.LeafPageCount * testCase.BackendPageSize; + expectedIndices = Enumerable.Range(0, documentCount).Reverse(); + } PartitionedFeedMode[] feedModes = new PartitionedFeedMode[] { @@ -561,8 +781,9 @@ private static async Task RunHybridSearchTest(HybridSearchTest testCase) expectedIndices = expectedIndices.Take(testCase.Take.Value); } + // Create container with ALL ranges (for statistics if Global scope) MockDocumentContainer nonStreamingDocumentContainer = MockDocumentContainer.CreateHybridSearchContainer( - ranges, + allRanges, feedModes, leafPageCount: testCase.LeafPageCount, backendPageSize: testCase.BackendPageSize, @@ -571,13 +792,15 @@ private static async Task RunHybridSearchTest(HybridSearchTest testCase) (IReadOnlyList results, double requestCharge) = await CreateAndRunHybridSearchQueryPipelineStage( documentContainer: nonStreamingDocumentContainer, - ranges: ranges, + targetRanges: targetRanges, + allRanges: allRanges, requiresGlobalStatistics: testCase.RequiresGlobalStatistics, pageSize: testCase.PageSize, skip: (uint?)testCase.Skip, take: (uint?)testCase.Take, weights: testCase.Weights, - skipOrderByRewrite: testCase.SkipOrderByRewrite); + skipOrderByRewrite: testCase.SkipOrderByRewrite, + fullTextScoreScope: testCase.FullTextScoreScope); Assert.AreEqual(expectedIndices.Count(), results.Count); @@ -599,6 +822,27 @@ private static async Task RunHybridSearchTest(HybridSearchTest testCase) } Assert.AreEqual(nonStreamingDocumentContainer.TotalRequestCharge, requestCharge); + + // Validate statistics query ranges + if (testCase.RequiresGlobalStatistics) + { + IReadOnlyList expectedStatisticsRanges = + testCase.FullTextScoreScope == FullTextScoreScope.Global + ? allRanges + : targetRanges; + + Assert.AreEqual( + expectedStatisticsRanges.Count, + nonStreamingDocumentContainer.StatisticsQueryRanges.Count, + $"Statistics query should have targeted {expectedStatisticsRanges.Count} ranges for {testCase.FullTextScoreScope} scope"); + + foreach (FeedRangeEpk expectedRange in expectedStatisticsRanges) + { + Assert.IsTrue( + nonStreamingDocumentContainer.StatisticsQueryRanges.Any(r => r.Equals(expectedRange)), + $"Statistics query should have targeted range {expectedRange}"); + } + } } private static Task RunParityTests( @@ -657,13 +901,15 @@ private static async Task RunParityTests( private static Task<(IReadOnlyList, double)> CreateAndRunHybridSearchQueryPipelineStage( IDocumentContainer documentContainer, - IReadOnlyList ranges, + IReadOnlyList targetRanges, + IReadOnlyList allRanges, bool requiresGlobalStatistics, int pageSize, uint? skip, uint? take, double[] weights, - bool skipOrderByRewrite) + bool skipOrderByRewrite, + FullTextScoreScope fullTextScoreScope) { HybridSearchQueryInfo hybridSearchQueryInfo = skipOrderByRewrite ? Create2ItemHybridSearchSkipOrderByRewriteQueryInfo(requiresGlobalStatistics, skip, take, weights) : @@ -672,15 +918,16 @@ private static async Task RunParityTests( TryCatch tryCreatePipeline = PipelineFactory.MonadicCreate( documentContainer, Create2ItemSqlQuerySpec(), - ranges, + targetRanges, partitionKey: null, queryInfo: null, hybridSearchQueryInfo: hybridSearchQueryInfo, maxItemCount: pageSize, new ContainerQueryProperties(), - ranges, + allRanges, isContinuationExpected: true, maxConcurrency: MaxConcurrency, + fullTextScoreScope: fullTextScoreScope, requestContinuationToken: null); Assert.IsTrue(tryCreatePipeline.Succeeded); @@ -854,7 +1101,9 @@ private static HybridSearchTest MakeHybridSearchTest( int? take, int pageSize, bool returnEmptyGlobalStatistics = false, - bool skipOrderByRewrite = false) + bool skipOrderByRewrite = false, + FullTextScoreScope fullTextScoreScope = FullTextScoreScope.Global, + int? targetRangeCount = null) { return new HybridSearchTest( leafPageCount, @@ -865,7 +1114,9 @@ private static HybridSearchTest MakeHybridSearchTest( weights: null, pageSize, returnEmptyGlobalStatistics, - skipOrderByRewrite); + skipOrderByRewrite, + fullTextScoreScope, + targetRangeCount); } private static HybridSearchTest MakeHybridSearchSkipOrderByRewriteTest( @@ -875,7 +1126,9 @@ private static HybridSearchTest MakeHybridSearchSkipOrderByRewriteTest( int? skip, int? take, int pageSize, - bool returnEmptyGlobalStatistics = false) + bool returnEmptyGlobalStatistics = false, + FullTextScoreScope fullTextScoreScope = FullTextScoreScope.Global, + int? targetRangeCount = null) { return new HybridSearchTest( leafPageCount, @@ -886,7 +1139,9 @@ private static HybridSearchTest MakeHybridSearchSkipOrderByRewriteTest( weights: null, pageSize, returnEmptyGlobalStatistics, - skipOrderByRewrite: true); + skipOrderByRewrite: true, + fullTextScoreScope, + targetRangeCount); } private static HybridSearchTest MakeHybridSearchTest( @@ -898,7 +1153,9 @@ private static HybridSearchTest MakeHybridSearchTest( double[] weights, int pageSize, bool returnEmptyGlobalStatistics = false, - bool skipOrderByRewrite = false) + bool skipOrderByRewrite = false, + FullTextScoreScope fullTextScoreScope = FullTextScoreScope.Global, + int? targetRangeCount = null) { return new HybridSearchTest( leafPageCount, @@ -909,7 +1166,9 @@ private static HybridSearchTest MakeHybridSearchTest( weights, pageSize, returnEmptyGlobalStatistics, - skipOrderByRewrite); + skipOrderByRewrite, + fullTextScoreScope, + targetRangeCount); } private static HybridSearchTest MakeHybridSearchSkipOrderByRewriteTest( @@ -920,7 +1179,9 @@ private static HybridSearchTest MakeHybridSearchSkipOrderByRewriteTest( int? take, double[] weights, int pageSize, - bool returnEmptyGlobalStatistics = false) + bool returnEmptyGlobalStatistics = false, + FullTextScoreScope fullTextScoreScope = FullTextScoreScope.Global, + int? targetRangeCount = null) { return new HybridSearchTest( leafPageCount, @@ -931,7 +1192,9 @@ private static HybridSearchTest MakeHybridSearchSkipOrderByRewriteTest( weights, pageSize, returnEmptyGlobalStatistics, - skipOrderByRewrite: true); + skipOrderByRewrite: true, + fullTextScoreScope, + targetRangeCount); } private class HybridSearchTest @@ -954,6 +1217,12 @@ private class HybridSearchTest public bool SkipOrderByRewrite { get; } + public FullTextScoreScope FullTextScoreScope { get; } + + // Number of ranges to use as target ranges (subset of all ranges) + // null means use all ranges (current behavior) + public int? TargetRangeCount { get; } + public HybridSearchTest( int leafPageCount, int backendPageSize, @@ -963,7 +1232,9 @@ public HybridSearchTest( double[] weights, int pageSize, bool returnEmptyGlobalStatistics, - bool skipOrderByRewrite) + bool skipOrderByRewrite, + FullTextScoreScope fullTextScoreScope = FullTextScoreScope.Global, + int? targetRangeCount = null) { this.LeafPageCount = leafPageCount; this.BackendPageSize = backendPageSize; @@ -974,6 +1245,8 @@ public HybridSearchTest( this.PageSize = pageSize; this.ReturnEmptyGlobalStatistics = returnEmptyGlobalStatistics; this.SkipOrderByRewrite = skipOrderByRewrite; + this.FullTextScoreScope = fullTextScoreScope; + this.TargetRangeCount = targetRangeCount; } } @@ -1346,10 +1619,14 @@ private class MockDocumentContainer : IDocumentContainer private readonly bool returnEmptyGlobalStatistics; + private readonly List statisticsQueryRanges = new List(); + private int statisticsQueryCount; private int queryCount; + public IReadOnlyList StatisticsQueryRanges => this.statisticsQueryRanges; + public double TotalRequestCharge { get @@ -1493,6 +1770,11 @@ public Task> MonadicQueryAsync(SqlQuerySpec sqlQuerySpec, Fe { if (this.isGlobalStatisticsQuery(sqlQuerySpec)) { + lock (this.statisticsQueryRanges) + { + this.statisticsQueryRanges.Add(feedRangeState.FeedRange); + } + QueryPage globalStatisticsPage = new QueryPage( documents: new List { this.returnEmptyGlobalStatistics ? CreateEmptyHybridSearchGlobalStatistics() : CreateHybridSearchGlobalStatistics() }, requestCharge: GlobalStatisticsQueryCharge, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/OrderByCrossPartitionQueryPipelineStageTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/OrderByCrossPartitionQueryPipelineStageTests.cs index 0d819b7bdd..00a646ba61 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/OrderByCrossPartitionQueryPipelineStageTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/OrderByCrossPartitionQueryPipelineStageTests.cs @@ -1,749 +1,749 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos.Tests.Query.Pipeline -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.CosmosElements; - using Microsoft.Azure.Cosmos.CosmosElements.Numbers; - using Microsoft.Azure.Cosmos.Pagination; - using Microsoft.Azure.Cosmos.Query.Core; - using Microsoft.Azure.Cosmos.Query.Core.Exceptions; - using Microsoft.Azure.Cosmos.Query.Core.Monads; - using Microsoft.Azure.Cosmos.Query.Core.Pipeline; - using Microsoft.Azure.Cosmos.Query.Core.Pipeline.CrossPartition.OrderBy; - using Microsoft.Azure.Cosmos.Query.Core.Pipeline.CrossPartition.Parallel; - using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination; - using Microsoft.Azure.Cosmos.Tests.Pagination; - using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Documents; - using Microsoft.Azure.Documents.Routing; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using Moq; - - [TestClass] - public class OrderByCrossPartitionQueryPipelineStageTests - { - private static IReadOnlyList<(string serializedToken, CosmosElement element)> TokenTestData() - { - Guid guid = Guid.Parse("69D5AB17-C94A-4173-A278-B59D0D9C7C37"); - byte[] randomBytes = guid.ToByteArray(); - string hexString = PartitionKeyInternal.HexConvert.ToHex(randomBytes, 0, randomBytes.Length); - - return new List<(string, CosmosElement)> - { - ("[42, 37]", CosmosArray.Parse("[42, 37]")), - ($@"{{C_Binary(""0x{hexString}"")}}", CosmosBinary.Create(new ReadOnlyMemory(randomBytes))), - ("false", CosmosBoolean.Create(false)), - ($@"{{C_Guid(""{guid}"")}}", CosmosGuid.Create(guid)), - ("null", CosmosNull.Create()), - ("1", CosmosInt64.Create(1)), - ("{\"foo\": false}", CosmosObject.Parse("{\"foo\": false}")), - ("asdf", CosmosString.Create("asdf")) - }; - } - - private static IReadOnlyList<(string serializedToken, SqlQueryResumeValue resumeValue)> ResumeValueTestData() - { - return new List<(string, SqlQueryResumeValue)> - { - ("[]", SqlQueryResumeValue.FromCosmosElement(CosmosUndefined.Create())), - ("null", SqlQueryResumeValue.FromCosmosElement(CosmosNull.Create())), - ("false", SqlQueryResumeValue.FromCosmosElement(CosmosBoolean.Create(false))), - ("true", SqlQueryResumeValue.FromCosmosElement(CosmosBoolean.Create(true))), - ("1337", SqlQueryResumeValue.FromCosmosElement(CosmosNumber64.Create(1337))), - ("asdf", SqlQueryResumeValue.FromCosmosElement(CosmosString.Create("asdf"))), - ("{\"type\":\"array\",\"low\":-6706074647855398782,\"high\":9031114912533472255}", SqlQueryResumeValue.FromOrderByValue(CosmosArray.Parse("[]"))), - ("{\"type\":\"object\",\"low\":1457042291250783704,\"high\":1493060239874959160}", SqlQueryResumeValue.FromOrderByValue(CosmosObject.Parse("{}"))) - }; - } - - [TestMethod] - public void MonadicCreate_NullContinuationToken() - { - Mock mockDocumentContainer = new Mock(); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: mockDocumentContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c._ts"), - targetRanges: new List() { FeedRangeEpk.FullRange }, - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("_ts", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: false, - continuationToken: null, +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Tests.Query.Pipeline +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.CosmosElements; + using Microsoft.Azure.Cosmos.CosmosElements.Numbers; + using Microsoft.Azure.Cosmos.Pagination; + using Microsoft.Azure.Cosmos.Query.Core; + using Microsoft.Azure.Cosmos.Query.Core.Exceptions; + using Microsoft.Azure.Cosmos.Query.Core.Monads; + using Microsoft.Azure.Cosmos.Query.Core.Pipeline; + using Microsoft.Azure.Cosmos.Query.Core.Pipeline.CrossPartition.OrderBy; + using Microsoft.Azure.Cosmos.Query.Core.Pipeline.CrossPartition.Parallel; + using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination; + using Microsoft.Azure.Cosmos.Tests.Pagination; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Routing; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; + + [TestClass] + public class OrderByCrossPartitionQueryPipelineStageTests + { + private static IReadOnlyList<(string serializedToken, CosmosElement element)> TokenTestData() + { + Guid guid = Guid.Parse("69D5AB17-C94A-4173-A278-B59D0D9C7C37"); + byte[] randomBytes = guid.ToByteArray(); + string hexString = PartitionKeyInternal.HexConvert.ToHex(randomBytes, 0, randomBytes.Length); + + return new List<(string, CosmosElement)> + { + ("[42, 37]", CosmosArray.Parse("[42, 37]")), + ($@"{{C_Binary(""0x{hexString}"")}}", CosmosBinary.Create(new ReadOnlyMemory(randomBytes))), + ("false", CosmosBoolean.Create(false)), + ($@"{{C_Guid(""{guid}"")}}", CosmosGuid.Create(guid)), + ("null", CosmosNull.Create()), + ("1", CosmosInt64.Create(1)), + ("{\"foo\": false}", CosmosObject.Parse("{\"foo\": false}")), + ("asdf", CosmosString.Create("asdf")) + }; + } + + private static IReadOnlyList<(string serializedToken, SqlQueryResumeValue resumeValue)> ResumeValueTestData() + { + return new List<(string, SqlQueryResumeValue)> + { + ("[]", SqlQueryResumeValue.FromCosmosElement(CosmosUndefined.Create())), + ("null", SqlQueryResumeValue.FromCosmosElement(CosmosNull.Create())), + ("false", SqlQueryResumeValue.FromCosmosElement(CosmosBoolean.Create(false))), + ("true", SqlQueryResumeValue.FromCosmosElement(CosmosBoolean.Create(true))), + ("1337", SqlQueryResumeValue.FromCosmosElement(CosmosNumber64.Create(1337))), + ("asdf", SqlQueryResumeValue.FromCosmosElement(CosmosString.Create("asdf"))), + ("{\"type\":\"array\",\"low\":-6706074647855398782,\"high\":9031114912533472255}", SqlQueryResumeValue.FromOrderByValue(CosmosArray.Parse("[]"))), + ("{\"type\":\"object\",\"low\":1457042291250783704,\"high\":1493060239874959160}", SqlQueryResumeValue.FromOrderByValue(CosmosObject.Parse("{}"))) + }; + } + + [TestMethod] + public void MonadicCreate_NullContinuationToken() + { + Mock mockDocumentContainer = new Mock(); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: mockDocumentContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c._ts"), + targetRanges: new List() { FeedRangeEpk.FullRange }, + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("_ts", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: false, + continuationToken: null, containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Succeeded); - } - - [TestMethod] - public void MonadicCreate_NonCosmosArrayContinuationToken() - { - Mock mockDocumentContainer = new Mock(); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: mockDocumentContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c._ts"), - targetRanges: new List() { FeedRangeEpk.FullRange }, - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("_ts", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: false, - continuationToken: CosmosObject.Create(new Dictionary()), + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Succeeded); + } + + [TestMethod] + public void MonadicCreate_NonCosmosArrayContinuationToken() + { + Mock mockDocumentContainer = new Mock(); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: mockDocumentContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c._ts"), + targetRanges: new List() { FeedRangeEpk.FullRange }, + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("_ts", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: false, + continuationToken: CosmosObject.Create(new Dictionary()), containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Failed); - Assert.IsTrue(monadicCreate.InnerMostException is MalformedContinuationTokenException); - } - - [TestMethod] - public void MonadicCreate_EmptyArrayContinuationToken() - { - Mock mockDocumentContainer = new Mock(); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: mockDocumentContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c._ts"), - targetRanges: new List() { FeedRangeEpk.FullRange }, - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("_ts", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: false, - continuationToken: CosmosArray.Create(new List()), + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Failed); + Assert.IsTrue(monadicCreate.InnerMostException is MalformedContinuationTokenException); + } + + [TestMethod] + public void MonadicCreate_EmptyArrayContinuationToken() + { + Mock mockDocumentContainer = new Mock(); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: mockDocumentContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c._ts"), + targetRanges: new List() { FeedRangeEpk.FullRange }, + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("_ts", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: false, + continuationToken: CosmosArray.Create(new List()), containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Failed); - Assert.IsTrue(monadicCreate.InnerMostException is MalformedContinuationTokenException); - } - - [TestMethod] - public void MonadicCreate_NonParallelContinuationToken() - { - Mock mockDocumentContainer = new Mock(); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: mockDocumentContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c._ts"), - targetRanges: new List() { FeedRangeEpk.FullRange }, - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("_ts", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: false, - continuationToken: CosmosArray.Create(new List() { CosmosString.Create("asdf") }), + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Failed); + Assert.IsTrue(monadicCreate.InnerMostException is MalformedContinuationTokenException); + } + + [TestMethod] + public void MonadicCreate_NonParallelContinuationToken() + { + Mock mockDocumentContainer = new Mock(); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: mockDocumentContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c._ts"), + targetRanges: new List() { FeedRangeEpk.FullRange }, + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("_ts", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: false, + continuationToken: CosmosArray.Create(new List() { CosmosString.Create("asdf") }), containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Failed); - Assert.IsTrue(monadicCreate.InnerMostException is MalformedContinuationTokenException); - } - - [TestMethod] - public void MonadicCreate_SingleOrderByContinuationToken() - { - Mock mockDocumentContainer = new Mock(); - IReadOnlyList<(string serializedToken, CosmosElement element)> tokens = TokenTestData(); - - foreach ((string serializedToken, CosmosElement element) in tokens) - { - ParallelContinuationToken parallelContinuationToken = new ParallelContinuationToken( - token: serializedToken, - range: new Documents.Routing.Range("A", "B", true, false)); - - OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken( - parallelContinuationToken, - new List() { new OrderByItem(CosmosObject.Create(new Dictionary() { { "item", element} })) }, - resumeValues: null, - rid: "rid", - skipCount: 42, - filter: "filter"); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: mockDocumentContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c.item"), - targetRanges: new List() { new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)) }, - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("item", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: false, - continuationToken: CosmosArray.Create( - new List() - { - OrderByContinuationToken.ToCosmosElement(orderByContinuationToken) - }), + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Failed); + Assert.IsTrue(monadicCreate.InnerMostException is MalformedContinuationTokenException); + } + + [TestMethod] + public void MonadicCreate_SingleOrderByContinuationToken() + { + Mock mockDocumentContainer = new Mock(); + IReadOnlyList<(string serializedToken, CosmosElement element)> tokens = TokenTestData(); + + foreach ((string serializedToken, CosmosElement element) in tokens) + { + ParallelContinuationToken parallelContinuationToken = new ParallelContinuationToken( + token: serializedToken, + range: new Documents.Routing.Range("A", "B", true, false)); + + OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken( + parallelContinuationToken, + new List() { new OrderByItem(CosmosObject.Create(new Dictionary() { { "item", element} })) }, + resumeValues: null, + rid: "rid", + skipCount: 42, + filter: "filter"); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: mockDocumentContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c.item"), + targetRanges: new List() { new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)) }, + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("item", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: false, + continuationToken: CosmosArray.Create( + new List() + { + OrderByContinuationToken.ToCosmosElement(orderByContinuationToken) + }), containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Succeeded); - } - - foreach ((string token1, CosmosElement element1) in tokens) - { - foreach ((string token2, CosmosElement element2) in tokens) - { - ParallelContinuationToken parallelContinuationToken1 = new ParallelContinuationToken( - token: $"[{token1}, {token2}]", - range: new Documents.Routing.Range("A", "B", true, false)); - - OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken( - parallelContinuationToken1, - new List() - { - new OrderByItem(CosmosObject.Create(new Dictionary(){ { "item1", element1 } })), - new OrderByItem(CosmosObject.Create(new Dictionary(){ { "item2", element2 } })) - }, - resumeValues: null, - rid: "rid", - skipCount: 42, - filter: "filter"); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: mockDocumentContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c.item1, c.item2"), - targetRanges: new List() - { - new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)), - new FeedRangeEpk(new Range(min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)), - }, - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("item1", SortOrder.Ascending), - new OrderByColumn("item2", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: false, - continuationToken: CosmosArray.Create( - new List() - { - OrderByContinuationToken.ToCosmosElement(orderByContinuationToken) - }), + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Succeeded); + } + + foreach ((string token1, CosmosElement element1) in tokens) + { + foreach ((string token2, CosmosElement element2) in tokens) + { + ParallelContinuationToken parallelContinuationToken1 = new ParallelContinuationToken( + token: $"[{token1}, {token2}]", + range: new Documents.Routing.Range("A", "B", true, false)); + + OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken( + parallelContinuationToken1, + new List() + { + new OrderByItem(CosmosObject.Create(new Dictionary(){ { "item1", element1 } })), + new OrderByItem(CosmosObject.Create(new Dictionary(){ { "item2", element2 } })) + }, + resumeValues: null, + rid: "rid", + skipCount: 42, + filter: "filter"); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: mockDocumentContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c.item1, c.item2"), + targetRanges: new List() + { + new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)), + new FeedRangeEpk(new Range(min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)), + }, + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("item1", SortOrder.Ascending), + new OrderByColumn("item2", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: false, + continuationToken: CosmosArray.Create( + new List() + { + OrderByContinuationToken.ToCosmosElement(orderByContinuationToken) + }), containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Succeeded); - } - } - } - - [TestMethod] - public void MonadicCreate_MultipleOrderByContinuationToken() - { - Mock mockDocumentContainer = new Mock(); - IReadOnlyList<(string serializedToken, CosmosElement element)> tokens = TokenTestData(); - - foreach((string token1, CosmosElement element1) in tokens) - { - foreach ((string token2, CosmosElement element2) in tokens) - { - ParallelContinuationToken parallelContinuationToken1 = new ParallelContinuationToken( - token: token1, - range: new Documents.Routing.Range("A", "B", true, false)); - - OrderByContinuationToken orderByContinuationToken1 = new OrderByContinuationToken( - parallelContinuationToken1, - new List() { new OrderByItem(CosmosObject.Create(new Dictionary() { { "item", element1 } })) }, - resumeValues: null, - rid: "rid", - skipCount: 42, - filter: "filter"); - - ParallelContinuationToken parallelContinuationToken2 = new ParallelContinuationToken( - token: token2, - range: new Documents.Routing.Range("B", "C", true, false)); - - OrderByContinuationToken orderByContinuationToken2 = new OrderByContinuationToken( - parallelContinuationToken2, - new List() { new OrderByItem(CosmosObject.Create(new Dictionary() { { "item", element2 } })) }, - resumeValues: null, - rid: "rid", - skipCount: 42, - filter: "filter"); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: mockDocumentContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c.item"), - targetRanges: new List() - { - new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)), - new FeedRangeEpk(new Range(min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)), - }, - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("item", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: false, - continuationToken: CosmosArray.Create( - new List() - { - OrderByContinuationToken.ToCosmosElement(orderByContinuationToken1), - OrderByContinuationToken.ToCosmosElement(orderByContinuationToken2) - }), + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Succeeded); + } + } + } + + [TestMethod] + public void MonadicCreate_MultipleOrderByContinuationToken() + { + Mock mockDocumentContainer = new Mock(); + IReadOnlyList<(string serializedToken, CosmosElement element)> tokens = TokenTestData(); + + foreach((string token1, CosmosElement element1) in tokens) + { + foreach ((string token2, CosmosElement element2) in tokens) + { + ParallelContinuationToken parallelContinuationToken1 = new ParallelContinuationToken( + token: token1, + range: new Documents.Routing.Range("A", "B", true, false)); + + OrderByContinuationToken orderByContinuationToken1 = new OrderByContinuationToken( + parallelContinuationToken1, + new List() { new OrderByItem(CosmosObject.Create(new Dictionary() { { "item", element1 } })) }, + resumeValues: null, + rid: "rid", + skipCount: 42, + filter: "filter"); + + ParallelContinuationToken parallelContinuationToken2 = new ParallelContinuationToken( + token: token2, + range: new Documents.Routing.Range("B", "C", true, false)); + + OrderByContinuationToken orderByContinuationToken2 = new OrderByContinuationToken( + parallelContinuationToken2, + new List() { new OrderByItem(CosmosObject.Create(new Dictionary() { { "item", element2 } })) }, + resumeValues: null, + rid: "rid", + skipCount: 42, + filter: "filter"); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: mockDocumentContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c.item"), + targetRanges: new List() + { + new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)), + new FeedRangeEpk(new Range(min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)), + }, + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("item", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: false, + continuationToken: CosmosArray.Create( + new List() + { + OrderByContinuationToken.ToCosmosElement(orderByContinuationToken1), + OrderByContinuationToken.ToCosmosElement(orderByContinuationToken2) + }), containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Succeeded); - } - } - } - - [TestMethod] - public void MonadicCreate_OrderByWithResumeValues() - { - Mock mockDocumentContainer = new Mock(); - IReadOnlyList<(string serializedToken, SqlQueryResumeValue resumeValue)> tokens = ResumeValueTestData(); - - foreach ((string serializedToken, SqlQueryResumeValue resumeValue) in tokens) - { - ParallelContinuationToken parallelContinuationToken = new ParallelContinuationToken( - token: serializedToken, - range: new Documents.Routing.Range("A", "B", true, false)); - - OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken( - parallelContinuationToken, - orderByItems: null, - resumeValues: new List() { resumeValue}, - rid: "rid", - skipCount: 42, - filter: null); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: mockDocumentContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c.item"), - targetRanges: new List() { new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)) }, - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("item", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: false, - continuationToken: CosmosArray.Create( - new List() - { - OrderByContinuationToken.ToCosmosElement(orderByContinuationToken) - }), + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Succeeded); + } + } + } + + [TestMethod] + public void MonadicCreate_OrderByWithResumeValues() + { + Mock mockDocumentContainer = new Mock(); + IReadOnlyList<(string serializedToken, SqlQueryResumeValue resumeValue)> tokens = ResumeValueTestData(); + + foreach ((string serializedToken, SqlQueryResumeValue resumeValue) in tokens) + { + ParallelContinuationToken parallelContinuationToken = new ParallelContinuationToken( + token: serializedToken, + range: new Documents.Routing.Range("A", "B", true, false)); + + OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken( + parallelContinuationToken, + orderByItems: null, + resumeValues: new List() { resumeValue}, + rid: "rid", + skipCount: 42, + filter: null); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: mockDocumentContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c.item"), + targetRanges: new List() { new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)) }, + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("item", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: false, + continuationToken: CosmosArray.Create( + new List() + { + OrderByContinuationToken.ToCosmosElement(orderByContinuationToken) + }), containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Succeeded); - } - - // Multiple resume values - foreach ((string token1, SqlQueryResumeValue resumeValue1) in tokens) - { - foreach ((string token2, SqlQueryResumeValue resumeValue2) in tokens) - { - ParallelContinuationToken parallelContinuationToken1 = new ParallelContinuationToken( - token: $"[{token1}, {token2}]", - range: new Documents.Routing.Range("A", "B", true, false)); - - OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken( - parallelContinuationToken1, - orderByItems: null, - new List() { resumeValue1, resumeValue2 }, - rid: "rid", - skipCount: 42, - filter: null); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: mockDocumentContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c.item1, c.item2"), - targetRanges: new List() - { - new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)), - new FeedRangeEpk(new Range(min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)), - }, - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("item1", SortOrder.Ascending), - new OrderByColumn("item2", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: false, - continuationToken: CosmosArray.Create( - new List() - { - OrderByContinuationToken.ToCosmosElement(orderByContinuationToken) - }), + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Succeeded); + } + + // Multiple resume values + foreach ((string token1, SqlQueryResumeValue resumeValue1) in tokens) + { + foreach ((string token2, SqlQueryResumeValue resumeValue2) in tokens) + { + ParallelContinuationToken parallelContinuationToken1 = new ParallelContinuationToken( + token: $"[{token1}, {token2}]", + range: new Documents.Routing.Range("A", "B", true, false)); + + OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken( + parallelContinuationToken1, + orderByItems: null, + new List() { resumeValue1, resumeValue2 }, + rid: "rid", + skipCount: 42, + filter: null); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: mockDocumentContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c ORDER BY c.item1, c.item2"), + targetRanges: new List() + { + new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)), + new FeedRangeEpk(new Range(min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)), + }, + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("item1", SortOrder.Ascending), + new OrderByColumn("item2", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: false, + continuationToken: CosmosArray.Create( + new List() + { + OrderByContinuationToken.ToCosmosElement(orderByContinuationToken) + }), containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Succeeded); - } - } - } - - [TestMethod] - public async Task TestFormattedFiltersForTargetPartitionWithContinuationTokenAsync() - { - QueryPage emptyPage = new QueryPage( - documents: new List(), - requestCharge: 0, - activityId: string.Empty, - cosmosQueryExecutionInfo: default, - distributionPlanSpec: default, - disallowContinuationTokenMessage: default, - additionalHeaders: default, - state: default, - streaming: default); - - string expectedQuerySpec = "SELECT * FROM c WHERE true ORDER BY c._ts"; - Mock mockContainer = new Mock(MockBehavior.Strict); - mockContainer - .Setup( - c => c.MonadicQueryAsync( - It.Is(sqlQuerySpec => expectedQuerySpec.Equals(sqlQuerySpec.QueryText) && sqlQuerySpec.ResumeFilter.ResumeValues.Count == 1), - It.IsAny>(), - It.IsAny(), - NoOpTrace.Singleton, - default)) - .ReturnsAsync(TryCatch.FromResult(emptyPage)); - - string continuationToken = @"[{""compositeToken"":{""token"":null,""range"":{""min"":""A"",""max"":""B""}},""orderByItems"":[{""item"":1665482200}],""rid"":""64kUAPYyHHk6XgIAAADACQ=="",""skipCount"":1,""filter"":""( c._ts >= 1665482198 OR IS_STRING(c._ts) OR IS_ARRAY(c._ts) OR IS_OBJECT(c._ts) )""}]"; - - IReadOnlyList targetRanges = new List() - { - new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)), - new FeedRangeEpk(new Range(min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)) - }; - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: mockContainer.Object, - sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c WHERE {documentdb-formattableorderbyquery-filter} ORDER BY c._ts"), - targetRanges: targetRanges, - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("c._ts", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 1), - maxConcurrency: 0, - nonStreamingOrderBy: false, - continuationToken: CosmosElement.Parse(continuationToken), + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Succeeded); + } + } + } + + [TestMethod] + public async Task TestFormattedFiltersForTargetPartitionWithContinuationTokenAsync() + { + QueryPage emptyPage = new QueryPage( + documents: new List(), + requestCharge: 0, + activityId: string.Empty, + cosmosQueryExecutionInfo: default, + distributionPlanSpec: default, + disallowContinuationTokenMessage: default, + additionalHeaders: default, + state: default, + streaming: default); + + string expectedQuerySpec = "SELECT * FROM c WHERE true ORDER BY c._ts"; + Mock mockContainer = new Mock(MockBehavior.Strict); + mockContainer + .Setup( + c => c.MonadicQueryAsync( + It.Is(sqlQuerySpec => expectedQuerySpec.Equals(sqlQuerySpec.QueryText) && sqlQuerySpec.ResumeFilter.ResumeValues.Count == 1), + It.IsAny>(), + It.IsAny(), + NoOpTrace.Singleton, + default)) + .ReturnsAsync(TryCatch.FromResult(emptyPage)); + + string continuationToken = @"[{""compositeToken"":{""token"":null,""range"":{""min"":""A"",""max"":""B""}},""orderByItems"":[{""item"":1665482200}],""rid"":""64kUAPYyHHk6XgIAAADACQ=="",""skipCount"":1,""filter"":""( c._ts >= 1665482198 OR IS_STRING(c._ts) OR IS_ARRAY(c._ts) OR IS_OBJECT(c._ts) )""}]"; + + IReadOnlyList targetRanges = new List() + { + new FeedRangeEpk(new Range(min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)), + new FeedRangeEpk(new Range(min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)) + }; + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: mockContainer.Object, + sqlQuerySpec: new SqlQuerySpec("SELECT * FROM c WHERE {documentdb-formattableorderbyquery-filter} ORDER BY c._ts"), + targetRanges: targetRanges, + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("c._ts", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 1), + maxConcurrency: 0, + nonStreamingOrderBy: false, + continuationToken: CosmosElement.Parse(continuationToken), containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Succeeded); - - IQueryPipelineStage queryPipelineStage = monadicCreate.Result; - for (int i = 0; i < targetRanges.Count; ++i) - { - Assert.IsTrue(await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)); - } - - Assert.IsFalse(await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)); - } - - [TestMethod] - [DataRow(false, DisplayName = "NonStreaming: false")] - [DataRow(true, DisplayName = "NonStreaming: true")] - public async Task TestDrainFully_StartFromBeginingAsync_NoDocuments(bool nonStreamingOrderBy) - { - int numItems = 0; - IDocumentContainer documentContainer = await CreateDocumentContainerAsync(numItems); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: documentContainer, - sqlQuerySpec: new SqlQuerySpec(@" - SELECT c._rid AS _rid, [{""item"": c._ts}] AS orderByItems, c AS payload - FROM c - WHERE {documentdb-formattableorderbyquery-filter} - ORDER BY c._ts"), - targetRanges: await documentContainer.GetFeedRangesAsync( - trace: NoOpTrace.Singleton, - cancellationToken: default), - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("c._ts", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: nonStreamingOrderBy, - continuationToken: null, + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Succeeded); + + IQueryPipelineStage queryPipelineStage = monadicCreate.Result; + for (int i = 0; i < targetRanges.Count; ++i) + { + Assert.IsTrue(await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)); + } + + Assert.IsFalse(await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)); + } + + [TestMethod] + [DataRow(false, DisplayName = "NonStreaming: false")] + [DataRow(true, DisplayName = "NonStreaming: true")] + public async Task TestDrainFully_StartFromBeginingAsync_NoDocuments(bool nonStreamingOrderBy) + { + int numItems = 0; + IDocumentContainer documentContainer = await CreateDocumentContainerAsync(numItems); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: documentContainer, + sqlQuerySpec: new SqlQuerySpec(@" + SELECT c._rid AS _rid, [{""item"": c._ts}] AS orderByItems, c AS payload + FROM c + WHERE {documentdb-formattableorderbyquery-filter} + ORDER BY c._ts"), + targetRanges: await documentContainer.GetFeedRangesAsync( + trace: NoOpTrace.Singleton, + cancellationToken: default), + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("c._ts", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: nonStreamingOrderBy, + continuationToken: null, containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Succeeded); - IQueryPipelineStage queryPipelineStage = monadicCreate.Result; - - List documents = new List(); - while (await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) - { - TryCatch tryGetQueryPage = queryPipelineStage.Current; - if (tryGetQueryPage.Failed) - { - Assert.Fail(tryGetQueryPage.Exception.ToString()); - } - - QueryPage queryPage = tryGetQueryPage.Result; - documents.AddRange(queryPage.Documents); - - if (!nonStreamingOrderBy) - { - Assert.AreEqual(42, queryPage.RequestCharge); - } - } - - Assert.AreEqual(numItems, documents.Count); - Assert.IsTrue(documents.OrderBy(document => ((CosmosObject)document)["_ts"]).ToList().SequenceEqual(documents)); - } - - [TestMethod] - public async Task TestDrain_IncludesResponseHeadersInQueryPage() - { - IDocumentContainer documentContainer = await CreateDocumentContainerAsync(10); - - TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: documentContainer, - sqlQuerySpec: new SqlQuerySpec(@" - SELECT c._rid AS _rid, [{""item"": c._ts}] AS orderByItems, c AS payload - FROM c - WHERE {documentdb-formattableorderbyquery-filter} - ORDER BY c._ts"), - targetRanges: await documentContainer.GetFeedRangesAsync( - trace: NoOpTrace.Singleton, - cancellationToken: default), - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("c._ts", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: false, - continuationToken: null, + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Succeeded); + IQueryPipelineStage queryPipelineStage = monadicCreate.Result; + + List documents = new List(); + while (await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) + { + TryCatch tryGetQueryPage = queryPipelineStage.Current; + if (tryGetQueryPage.Failed) + { + Assert.Fail(tryGetQueryPage.Exception.ToString()); + } + + QueryPage queryPage = tryGetQueryPage.Result; + documents.AddRange(queryPage.Documents); + + if (!nonStreamingOrderBy) + { + Assert.AreEqual(42, queryPage.RequestCharge); + } + } + + Assert.AreEqual(numItems, documents.Count); + Assert.IsTrue(documents.OrderBy(document => ((CosmosObject)document)["_ts"]).ToList().SequenceEqual(documents)); + } + + [TestMethod] + public async Task TestDrain_IncludesResponseHeadersInQueryPage() + { + IDocumentContainer documentContainer = await CreateDocumentContainerAsync(10); + + TryCatch monadicCreate = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: documentContainer, + sqlQuerySpec: new SqlQuerySpec(@" + SELECT c._rid AS _rid, [{""item"": c._ts}] AS orderByItems, c AS payload + FROM c + WHERE {documentdb-formattableorderbyquery-filter} + ORDER BY c._ts"), + targetRanges: await documentContainer.GetFeedRangesAsync( + trace: NoOpTrace.Singleton, + cancellationToken: default), + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("c._ts", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: false, + continuationToken: null, containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - Assert.IsTrue(monadicCreate.Succeeded); - IQueryPipelineStage queryPipelineStage = monadicCreate.Result; - - int countAdditionalHeadersReceived = 0; - while (await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) - { - TryCatch tryGetQueryPage = queryPipelineStage.Current; - if (tryGetQueryPage.Failed) - { - Assert.Fail(tryGetQueryPage.Exception.ToString()); - } - - QueryPage queryPage = tryGetQueryPage.Result; - if (queryPage.AdditionalHeaders.Count > 0) - { - ++countAdditionalHeadersReceived; - } - } - - int countFeedRanges = (await documentContainer.GetFeedRangesAsync( - trace: NoOpTrace.Singleton, - cancellationToken: default)) - .Count; - Assert.IsTrue(countAdditionalHeadersReceived >= countFeedRanges); - } - - [TestMethod] - [DataRow(false, false, false, false, DisplayName = "NonStreaming: false, Use State: false, Allow Splits: false, Allow Merges: false")] - [DataRow(false, false, false, true, DisplayName = "NonStreaming: false, Use State: false, Allow Splits: false, Allow Merges: true")] - [DataRow(false, false, true, false, DisplayName = "NonStreaming: false, Use State: false, Allow Splits: true, Allow Merges: false")] - [DataRow(false, false, true, true, DisplayName = "NonStreaming: false, Use State: false, Allow Splits: true, Allow Merges: true")] - [DataRow(false, true, false, false, DisplayName = "NonStreaming: false, Use State: true, Allow Splits: false, Allow Merges: false")] - [DataRow(false, true, false, true, DisplayName = "NonStreaming: false, Use State: true, Allow Splits: false, Allow Merges: true")] - [DataRow(false, true, true, false, DisplayName = "NonStreaming: false, Use State: true, Allow Splits: true, Allow Merges: false")] - [DataRow(false, true, true, true, DisplayName = "NonStreaming: false, Use State: true, Allow Splits: true, Allow Merges: true")] - [DataRow(true, false, false, false, DisplayName = "NonStreaming: true, Use State: false, Allow Splits: false, Allow Merges: false")] - [DataRow(true, false, false, true, DisplayName = "NonStreaming: true, Use State: false, Allow Splits: false, Allow Merges: true")] - [DataRow(true, false, true, false, DisplayName = "NonStreaming: true, Use State: false, Allow Splits: true, Allow Merges: false")] - [DataRow(true, false, true, true, DisplayName = "NonStreaming: true, Use State: false, Allow Splits: true, Allow Merges: true")] - public async Task TestDrainWithStateSplitsAndMergeAsync(bool nonStreamingOrderBy, bool useState, bool allowSplits, bool allowMerges) - { - static async Task CreatePipelineStateAsync(IDocumentContainer documentContainer, CosmosElement continuationToken, bool nonStreamingOrderBy) - { - TryCatch monadicQueryPipelineStage = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( - documentContainer: documentContainer, - sqlQuerySpec: new SqlQuerySpec(@" - SELECT c._rid AS _rid, [{""item"": c.pk}] AS orderByItems, c AS payload - FROM c - WHERE {documentdb-formattableorderbyquery-filter} - ORDER BY c.pk"), - targetRanges: await documentContainer.GetFeedRangesAsync( - trace: NoOpTrace.Singleton, - cancellationToken: default), - partitionKey: null, - orderByColumns: new List() - { - new OrderByColumn("c.pk", SortOrder.Ascending) - }, - queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), - maxConcurrency: 10, - nonStreamingOrderBy: nonStreamingOrderBy, - continuationToken: continuationToken, + emitRawOrderByPayload: false); + Assert.IsTrue(monadicCreate.Succeeded); + IQueryPipelineStage queryPipelineStage = monadicCreate.Result; + + int countAdditionalHeadersReceived = 0; + while (await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) + { + TryCatch tryGetQueryPage = queryPipelineStage.Current; + if (tryGetQueryPage.Failed) + { + Assert.Fail(tryGetQueryPage.Exception.ToString()); + } + + QueryPage queryPage = tryGetQueryPage.Result; + if (queryPage.AdditionalHeaders.Count > 0) + { + ++countAdditionalHeadersReceived; + } + } + + int countFeedRanges = (await documentContainer.GetFeedRangesAsync( + trace: NoOpTrace.Singleton, + cancellationToken: default)) + .Count; + Assert.IsTrue(countAdditionalHeadersReceived >= countFeedRanges); + } + + [TestMethod] + [DataRow(false, false, false, false, DisplayName = "NonStreaming: false, Use State: false, Allow Splits: false, Allow Merges: false")] + [DataRow(false, false, false, true, DisplayName = "NonStreaming: false, Use State: false, Allow Splits: false, Allow Merges: true")] + [DataRow(false, false, true, false, DisplayName = "NonStreaming: false, Use State: false, Allow Splits: true, Allow Merges: false")] + [DataRow(false, false, true, true, DisplayName = "NonStreaming: false, Use State: false, Allow Splits: true, Allow Merges: true")] + [DataRow(false, true, false, false, DisplayName = "NonStreaming: false, Use State: true, Allow Splits: false, Allow Merges: false")] + [DataRow(false, true, false, true, DisplayName = "NonStreaming: false, Use State: true, Allow Splits: false, Allow Merges: true")] + [DataRow(false, true, true, false, DisplayName = "NonStreaming: false, Use State: true, Allow Splits: true, Allow Merges: false")] + [DataRow(false, true, true, true, DisplayName = "NonStreaming: false, Use State: true, Allow Splits: true, Allow Merges: true")] + [DataRow(true, false, false, false, DisplayName = "NonStreaming: true, Use State: false, Allow Splits: false, Allow Merges: false")] + [DataRow(true, false, false, true, DisplayName = "NonStreaming: true, Use State: false, Allow Splits: false, Allow Merges: true")] + [DataRow(true, false, true, false, DisplayName = "NonStreaming: true, Use State: false, Allow Splits: true, Allow Merges: false")] + [DataRow(true, false, true, true, DisplayName = "NonStreaming: true, Use State: false, Allow Splits: true, Allow Merges: true")] + public async Task TestDrainWithStateSplitsAndMergeAsync(bool nonStreamingOrderBy, bool useState, bool allowSplits, bool allowMerges) + { + static async Task CreatePipelineStateAsync(IDocumentContainer documentContainer, CosmosElement continuationToken, bool nonStreamingOrderBy) + { + TryCatch monadicQueryPipelineStage = OrderByCrossPartitionQueryPipelineStage.MonadicCreate( + documentContainer: documentContainer, + sqlQuerySpec: new SqlQuerySpec(@" + SELECT c._rid AS _rid, [{""item"": c.pk}] AS orderByItems, c AS payload + FROM c + WHERE {documentdb-formattableorderbyquery-filter} + ORDER BY c.pk"), + targetRanges: await documentContainer.GetFeedRangesAsync( + trace: NoOpTrace.Singleton, + cancellationToken: default), + partitionKey: null, + orderByColumns: new List() + { + new OrderByColumn("c.pk", SortOrder.Ascending) + }, + queryPaginationOptions: new QueryExecutionOptions(pageSizeHint: 10), + maxConcurrency: 10, + nonStreamingOrderBy: nonStreamingOrderBy, + continuationToken: continuationToken, containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - emitRawOrderByPayload: false); - monadicQueryPipelineStage.ThrowIfFailed(); - IQueryPipelineStage queryPipelineStage = monadicQueryPipelineStage.Result; - - return queryPipelineStage; - } - - bool verbose = false; - int numItems = 1000; - IDocumentContainer inMemoryCollection = await CreateDocumentContainerAsync(numItems); - IQueryPipelineStage queryPipelineStage = await CreatePipelineStateAsync(inMemoryCollection, continuationToken: null, nonStreamingOrderBy); - List documents = new List(); - Random random = new Random(); - while (await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) - { - TryCatch tryGetPage = queryPipelineStage.Current; - tryGetPage.ThrowIfFailed(); - - documents.AddRange(tryGetPage.Result.Documents); - - if (useState) - { - QueryPage queryPage; - QueryState queryState = null; - do - { - // We need to drain out all the initial empty pages, - // since they are non resumable state. - Assert.IsTrue(await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)); - TryCatch tryGetQueryPage = queryPipelineStage.Current; - if (tryGetQueryPage.Failed) - { - Assert.Fail(tryGetQueryPage.Exception.ToString()); - } - - queryPage = tryGetQueryPage.Result; - documents.AddRange(queryPage.Documents); - queryState = queryPage.State; - } while ((queryPage.Documents.Count == 0) && (queryState != null)); - - if (queryState == null) - { - break; - } - - queryPipelineStage = await CreatePipelineStateAsync(inMemoryCollection, queryState.Value, nonStreamingOrderBy); - } - - if (random.Next() % 2 == 0) - { - if (allowSplits && (random.Next() % 2 == 0)) - { - // Split - await inMemoryCollection.RefreshProviderAsync(NoOpTrace.Singleton, cancellationToken: default); - List ranges = await inMemoryCollection.GetFeedRangesAsync( - trace: NoOpTrace.Singleton, - cancellationToken: default); - FeedRangeInternal randomRangeToSplit = ranges[random.Next(0, ranges.Count)]; - await inMemoryCollection.SplitAsync(randomRangeToSplit, cancellationToken: default); - - if (verbose) - { - System.Diagnostics.Trace.WriteLine($"Split range: {randomRangeToSplit.ToJsonString()}"); - } - } - - if (allowMerges && (random.Next() % 2 == 0)) - { - // Merge - await inMemoryCollection.RefreshProviderAsync(NoOpTrace.Singleton, cancellationToken: default); - List ranges = await inMemoryCollection.GetFeedRangesAsync( - trace: NoOpTrace.Singleton, - cancellationToken: default); - if (ranges.Count > 1) - { - ranges = ranges.OrderBy(range => range.Range.Min).ToList(); - int indexToMerge = random.Next(0, ranges.Count); - int adjacentIndex = indexToMerge == (ranges.Count - 1) ? indexToMerge - 1 : indexToMerge + 1; - await inMemoryCollection.MergeAsync(ranges[indexToMerge], ranges[adjacentIndex], cancellationToken: default); - } - - if (verbose) - { - string mergedRanges = string.Join(", ", ranges.Select(range => range.ToJsonString())); - System.Diagnostics.Trace.WriteLine($"Merged ranges: {mergedRanges}"); - } - } - } - } - - Assert.AreEqual(numItems, documents.Count); - } - - private static async Task CreateDocumentContainerAsync( - int numItems, - FlakyDocumentContainer.FailureConfigs failureConfigs = null) - { - PartitionKeyDefinition partitionKeyDefinition = new PartitionKeyDefinition() - { - Paths = new System.Collections.ObjectModel.Collection() - { - "/pk" - }, - Kind = PartitionKind.Hash, - Version = PartitionKeyDefinitionVersion.V2, - }; - - IMonadicDocumentContainer monadicDocumentContainer = new InMemoryContainer(partitionKeyDefinition); - if (failureConfigs != null) - { - monadicDocumentContainer = new FlakyDocumentContainer(monadicDocumentContainer, failureConfigs); - } - - DocumentContainer documentContainer = new DocumentContainer(monadicDocumentContainer); - - for (int i = 0; i < 3; i++) - { - await documentContainer.RefreshProviderAsync(NoOpTrace.Singleton, cancellationToken: default); - IReadOnlyList ranges = await documentContainer.GetFeedRangesAsync( - trace: NoOpTrace.Singleton, - cancellationToken: default); - foreach (FeedRangeInternal range in ranges) - { - await documentContainer.SplitAsync(range, cancellationToken: default); - } - } - - for (int i = 0; i < numItems; i++) - { - // Insert an item - CosmosObject item = CosmosObject.Parse($"{{\"pk\" : {i} }}"); - while (true) - { - TryCatch monadicCreateRecord = await documentContainer.MonadicCreateItemAsync(item, cancellationToken: default); - if (monadicCreateRecord.Succeeded) - { - break; - } - } - } - - return documentContainer; - } - } -} + emitRawOrderByPayload: false); + monadicQueryPipelineStage.ThrowIfFailed(); + IQueryPipelineStage queryPipelineStage = monadicQueryPipelineStage.Result; + + return queryPipelineStage; + } + + bool verbose = false; + int numItems = 1000; + IDocumentContainer inMemoryCollection = await CreateDocumentContainerAsync(numItems); + IQueryPipelineStage queryPipelineStage = await CreatePipelineStateAsync(inMemoryCollection, continuationToken: null, nonStreamingOrderBy); + List documents = new List(); + Random random = new Random(); + while (await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)) + { + TryCatch tryGetPage = queryPipelineStage.Current; + tryGetPage.ThrowIfFailed(); + + documents.AddRange(tryGetPage.Result.Documents); + + if (useState) + { + QueryPage queryPage; + QueryState queryState = null; + do + { + // We need to drain out all the initial empty pages, + // since they are non resumable state. + Assert.IsTrue(await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton, cancellationToken: default)); + TryCatch tryGetQueryPage = queryPipelineStage.Current; + if (tryGetQueryPage.Failed) + { + Assert.Fail(tryGetQueryPage.Exception.ToString()); + } + + queryPage = tryGetQueryPage.Result; + documents.AddRange(queryPage.Documents); + queryState = queryPage.State; + } while ((queryPage.Documents.Count == 0) && (queryState != null)); + + if (queryState == null) + { + break; + } + + queryPipelineStage = await CreatePipelineStateAsync(inMemoryCollection, queryState.Value, nonStreamingOrderBy); + } + + if (random.Next() % 2 == 0) + { + if (allowSplits && (random.Next() % 2 == 0)) + { + // Split + await inMemoryCollection.RefreshProviderAsync(NoOpTrace.Singleton, cancellationToken: default); + List ranges = await inMemoryCollection.GetFeedRangesAsync( + trace: NoOpTrace.Singleton, + cancellationToken: default); + FeedRangeInternal randomRangeToSplit = ranges[random.Next(0, ranges.Count)]; + await inMemoryCollection.SplitAsync(randomRangeToSplit, cancellationToken: default); + + if (verbose) + { + System.Diagnostics.Trace.WriteLine($"Split range: {randomRangeToSplit.ToJsonString()}"); + } + } + + if (allowMerges && (random.Next() % 2 == 0)) + { + // Merge + await inMemoryCollection.RefreshProviderAsync(NoOpTrace.Singleton, cancellationToken: default); + List ranges = await inMemoryCollection.GetFeedRangesAsync( + trace: NoOpTrace.Singleton, + cancellationToken: default); + if (ranges.Count > 1) + { + ranges = ranges.OrderBy(range => range.Range.Min).ToList(); + int indexToMerge = random.Next(0, ranges.Count); + int adjacentIndex = indexToMerge == (ranges.Count - 1) ? indexToMerge - 1 : indexToMerge + 1; + await inMemoryCollection.MergeAsync(ranges[indexToMerge], ranges[adjacentIndex], cancellationToken: default); + } + + if (verbose) + { + string mergedRanges = string.Join(", ", ranges.Select(range => range.ToJsonString())); + System.Diagnostics.Trace.WriteLine($"Merged ranges: {mergedRanges}"); + } + } + } + } + + Assert.AreEqual(numItems, documents.Count); + } + + private static async Task CreateDocumentContainerAsync( + int numItems, + FlakyDocumentContainer.FailureConfigs failureConfigs = null) + { + PartitionKeyDefinition partitionKeyDefinition = new PartitionKeyDefinition() + { + Paths = new System.Collections.ObjectModel.Collection() + { + "/pk" + }, + Kind = PartitionKind.Hash, + Version = PartitionKeyDefinitionVersion.V2, + }; + + IMonadicDocumentContainer monadicDocumentContainer = new InMemoryContainer(partitionKeyDefinition); + if (failureConfigs != null) + { + monadicDocumentContainer = new FlakyDocumentContainer(monadicDocumentContainer, failureConfigs); + } + + DocumentContainer documentContainer = new DocumentContainer(monadicDocumentContainer); + + for (int i = 0; i < 3; i++) + { + await documentContainer.RefreshProviderAsync(NoOpTrace.Singleton, cancellationToken: default); + IReadOnlyList ranges = await documentContainer.GetFeedRangesAsync( + trace: NoOpTrace.Singleton, + cancellationToken: default); + foreach (FeedRangeInternal range in ranges) + { + await documentContainer.SplitAsync(range, cancellationToken: default); + } + } + + for (int i = 0; i < numItems; i++) + { + // Insert an item + CosmosObject item = CosmosObject.Parse($"{{\"pk\" : {i} }}"); + while (true) + { + TryCatch monadicCreateRecord = await documentContainer.MonadicCreateItemAsync(item, cancellationToken: default); + if (monadicCreateRecord.Succeeded) + { + break; + } + } + } + + return documentContainer; + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/ParallelCrossPartitionQueryPipelineStageTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/ParallelCrossPartitionQueryPipelineStageTests.cs index 248058d2ef..9a64210f4f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/ParallelCrossPartitionQueryPipelineStageTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/ParallelCrossPartitionQueryPipelineStageTests.cs @@ -148,12 +148,8 @@ public void MonadicCreate_MultipleParallelContinuationToken() containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), maxConcurrency: 10, prefetchPolicy: PrefetchPolicy.PrefetchSinglePage, - continuationToken: CosmosArray.Create( - new List() - { - ParallelContinuationToken.ToCosmosElement(token1), - ParallelContinuationToken.ToCosmosElement(token2) - })); + continuationToken: CosmosArray.Create(new List() { ParallelContinuationToken.ToCosmosElement(token1), ParallelContinuationToken.ToCosmosElement(token2) })); + Assert.IsTrue(monadicCreate.Succeeded); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanBaselineTests.cs index 3b12b2f4ba..f8f6cf1905 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanBaselineTests.cs @@ -1173,27 +1173,27 @@ public void Top() Hash( @"TOP value beyond lower range - hybrid search", - @"SELECT TOP -1 c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) ", + @"SELECT TOP -1 c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') ", @"/key"), Hash( @"TOP value at lower range - hybrid search", - @"SELECT TOP 0 c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) ", + @"SELECT TOP 0 c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') ", @"/key"), Hash( @"TOP value at upper range (client) - hybrid search", - @"SELECT TOP 2147483647 c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) ", + @"SELECT TOP 2147483647 c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') ", @"/key"), Hash( @"TOP value beyond upper range (client) - hybrid search", - @"SELECT TOP 2147483648 c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) ", + @"SELECT TOP 2147483648 c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') ", @"/key"), Hash( @"TOP value beyond upper range (Interop) - hybrid search", - @"SELECT TOP 4294967296 c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) ", + @"SELECT TOP 4294967296 c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') ", @"/key") }; @@ -1285,27 +1285,27 @@ public void OffsetLimit() Hash( @"OFFSET value beyond lower range - hybrid search", - @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET -1 LIMIT 10", + @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET -1 LIMIT 10", @"/key"), Hash( @"OFFSET value at lower range - hybrid search", - @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 0 LIMIT 10", + @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 0 LIMIT 10", @"/key"), Hash( @"OFFSET value at upper range (client) - hybrid search", - @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 2147483647 LIMIT 10", + @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 2147483647 LIMIT 10", @"/key"), Hash( @"OFFSET value beyond upper range (client) - hybrid search", - @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 2147483648 LIMIT 10", + @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 2147483648 LIMIT 10", @"/key"), Hash( @"OFFSET value beyond upper range (Interop) - hybrid search", - @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 4294967296 LIMIT 10", + @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 4294967296 LIMIT 10", @"/key"), Hash( @@ -1335,27 +1335,27 @@ public void OffsetLimit() Hash( @"LIMIT value beyond lower range - hybrid search", - @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 10 LIMIT -1", + @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 10 LIMIT -1", @"/key"), Hash( @"LIMIT value at lower range - hybrid search", - @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 10 LIMIT 0", + @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 10 LIMIT 0", @"/key"), Hash( @"LIMIT value at upper range (client) - hybrid search", - @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 10 LIMIT 2147483647", + @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 10 LIMIT 2147483647", @"/key"), Hash( @"LIMIT value beyond upper range (client) - hybrid search", - @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 10 LIMIT 2147483648", + @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 10 LIMIT 2147483648", @"/key"), Hash( @"LIMIT value beyond upper range (Interop) - hybrid search", - @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, ['swim']) OFFSET 10 LIMIT 4294967296", + @"SELECT c.name FROM c ORDER BY RANK FullTextScore(c.text, 'swim') OFFSET 10 LIMIT 4294967296", @"/key"), }; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanRetrieverTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanRetrieverTests.cs index f869915c3a..590c0de933 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanRetrieverTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanRetrieverTests.cs @@ -374,10 +374,10 @@ public async Task SanityTests() query: "SELECT c.zipcode, COUNTIF(c.valid) AS valid_count FROM c GROUP BY c.zipcode", expected: @"{""distinctType"":""None"",""top"":null,""offset"":null,""limit"":null,""orderBy"":[],""orderByExpressions"":[],""groupByExpressions"":[""c.zipcode""],""groupByAliases"":[""zipcode"",""valid_count""],""aggregates"":[],""groupByAliasToAggregateType"":{""zipcode"":null,""valid_count"":""CountIf""},""rewrittenQuery"":""SELECT [{\""item\"": c.zipcode}] AS groupByItems, {\""zipcode\"": c.zipcode, \""valid_count\"": {\""item\"": COUNTIF(c.valid)}} AS payload\nFROM c\nGROUP BY c.zipcode"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":false}"), MakeTest( - query: "SELECT TOP 10 * FROM c ORDER BY RANK FullTextScore(c.text, ['swim', 'run'])", + query: "SELECT TOP 10 * FROM c ORDER BY RANK FullTextScore(c.text, \"swim\", \"run\")", expected: @"{""globalStatisticsQuery"":""SELECT COUNT(1) AS documentCount, [{\""totalWordCount\"": SUM(_FullTextWordCount(c.text)), \""hitCounts\"": [COUNTIF(FullTextContains(c.text, \""swim\"")), COUNTIF(FullTextContains(c.text, \""run\""))]}] AS fullTextStatistics\nFROM c"",""componentQueryInfos"":[{""distinctType"":""None"",""top"":120,""offset"":null,""limit"":null,""orderBy"":[""Descending""],""orderByExpressions"":[""_FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0})""],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, [{\""item\"": _FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0})}] AS orderByItems, {\""payload\"": c, \""componentScores\"": [(_FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) ?? -1)]} AS payload\nFROM c\nWHERE ({documentdb-formattableorderbyquery-filter})\nORDER BY _FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) DESC"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":true}],""componentWithoutPayloadQueryInfos"":[],""projectionQueryInfo"":null,""componentWeights"":[],""skip"":null,""take"":10,""requiresGlobalStatistics"":true}"), MakeTest( - query: "SELECT TOP 10 * FROM c ORDER BY RANK RRF(FullTextScore(c.text, ['swim', 'run']), VectorDistance(c.image, [1, 2, 3]))", + query: "SELECT TOP 10 * FROM c ORDER BY RANK RRF(FullTextScore(c.text, \"swim\", \"run\"), VectorDistance(c.image, [1, 2, 3]))", expected: @"{""globalStatisticsQuery"":""SELECT COUNT(1) AS documentCount, [{\""totalWordCount\"": SUM(_FullTextWordCount(c.text)), \""hitCounts\"": [COUNTIF(FullTextContains(c.text, \""swim\"")), COUNTIF(FullTextContains(c.text, \""run\""))]}] AS fullTextStatistics\nFROM c"",""componentQueryInfos"":[{""distinctType"":""None"",""top"":120,""offset"":null,""limit"":null,""orderBy"":[""Descending""],""orderByExpressions"":[""_FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0})""],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, [{\""item\"": _FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0})}] AS orderByItems, {\""payload\"": c, \""componentScores\"": [(_FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) ?? -1), (_VectorScore(c.image, [1, 2, 3]) ?? -1.79769e+308)]} AS payload\nFROM c\nWHERE ({documentdb-formattableorderbyquery-filter})\nORDER BY _FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) DESC"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":true},{""distinctType"":""None"",""top"":120,""offset"":null,""limit"":null,""orderBy"":[""Descending""],""orderByExpressions"":[""_VectorScore(c.image, [1, 2, 3])""],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, [{\""item\"": _VectorScore(c.image, [1, 2, 3])}] AS orderByItems, {\""payload\"": c, \""componentScores\"": [(_FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) ?? -1), (_VectorScore(c.image, [1, 2, 3]) ?? -1.79769e+308)]} AS payload\nFROM c\nWHERE ({documentdb-formattableorderbyquery-filter})\nORDER BY _VectorScore(c.image, [1, 2, 3])"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":true}],""componentWithoutPayloadQueryInfos"":[],""projectionQueryInfo"":null,""componentWeights"":[],""skip"":null,""take"":10,""requiresGlobalStatistics"":true}"), MakeTest( query: "SELECT TOP 10 * FROM c ORDER BY RANK RRF(VectorDistance(c.backup_image, [0.5, 0.2, 0.33]), VectorDistance(c.image, [1, 2, 3]))", @@ -389,17 +389,17 @@ public async Task SanityTests() query: "SELECT COUNTIF(_FullTextWordCount(c.abstract) > 10), COUNTIF(FullTextContains(c.abstract, 'inspiration')) FROM c", expected: @"{""distinctType"":""None"",""top"":null,""offset"":null,""limit"":null,""orderBy"":[],""orderByExpressions"":[],""groupByExpressions"":[],""groupByAliases"":[""$1"",""$2""],""aggregates"":[],""groupByAliasToAggregateType"":{""$1"":""CountIf"",""$2"":""CountIf""},""rewrittenQuery"":""SELECT {\""$1\"": {\""item\"": COUNTIF((_FullTextWordCount(c.abstract) > 10))}, \""$2\"": {\""item\"": COUNTIF(FullTextContains(c.abstract, \""inspiration\""))}} AS payload\nFROM c"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":false}"), MakeHybridSearchOptimizationTest( - query: "SELECT TOP 10 * FROM c ORDER BY RANK FullTextScore(c.text, 'swim', 'run')", + query: "SELECT TOP 10 * FROM c ORDER BY RANK FullTextScore(c.text, \"swim\", \"run\")", expected: @"{""globalStatisticsQuery"":""SELECT COUNT(1) AS documentCount, [{\""totalWordCount\"": SUM(_FullTextWordCount(c.text)), \""hitCounts\"": [COUNTIF(FullTextContains(c.text, \""swim\"")), COUNTIF(FullTextContains(c.text, \""run\""))]}] AS fullTextStatistics\nFROM c"",""componentQueryInfos"":[{""distinctType"":""None"",""top"":120,""offset"":null,""limit"":null,""orderBy"":[""Descending""],""orderByExpressions"":[""_FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0})""],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, [{\""item\"": _FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0})}] AS orderByItems, {\""payload\"": c, \""componentScores\"": []} AS payload\nFROM c\nWHERE ({documentdb-formattableorderbyquery-filter})\nORDER BY _FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) DESC"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":true}],""componentWithoutPayloadQueryInfos"":[],""projectionQueryInfo"":null,""componentWeights"":[],""skip"":null,""take"":10,""requiresGlobalStatistics"":true}"), MakeHybridSearchOptimizationTest( - query: "SELECT TOP 10 * FROM c ORDER BY RANK RRF(FullTextScore(c.text, 'swim', 'run'), VectorDistance(c.image, [1, 2, 3]))", - expected: @"{""globalStatisticsQuery"":""SELECT COUNT(1) AS documentCount, [{\""totalWordCount\"": SUM(_FullTextWordCount(c.text)), \""hitCounts\"": [COUNTIF(FullTextContains(c.text, \""swim\"")), COUNTIF(FullTextContains(c.text, \""run\""))]}] AS fullTextStatistics\nFROM c"",""componentQueryInfos"":[{""distinctType"":""None"",""top"":120,""offset"":null,""limit"":null,""orderBy"":[],""orderByExpressions"":[],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, c AS payload, [(_FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) ?? -1), (_VectorScore(c.image, [1, 2, 3]) ?? -1.79769e+308)] AS componentScores\nFROM c\nORDER BY _FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) DESC"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":false},{""distinctType"":""None"",""top"":120,""offset"":null,""limit"":null,""orderBy"":[],""orderByExpressions"":[],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, c AS payload, [(_FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) ?? -1), (_VectorScore(c.image, [1, 2, 3]) ?? -1.79769e+308)] AS componentScores\nFROM c\nORDER BY _VectorScore(c.image, [1, 2, 3])"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":false}],""componentWithoutPayloadQueryInfos"":[],""projectionQueryInfo"":null,""componentWeights"":[],""skip"":null,""take"":10,""requiresGlobalStatistics"":true}"), + query: "SELECT TOP 10 * FROM c ORDER BY RANK RRF(FullTextScore(c.text, \"swim\", \"run\"), VectorDistance(c.image, [1, 2, 3]))", + expected: @"{""globalStatisticsQuery"":""SELECT COUNT(1) AS documentCount, [{\""totalWordCount\"": SUM(_FullTextWordCount(c.text)), \""hitCounts\"": [COUNTIF(FullTextContains(c.text, \""swim\"")), COUNTIF(FullTextContains(c.text, \""run\""))]}] AS fullTextStatistics\nFROM c"",""componentQueryInfos"":[{""distinctType"":""None"",""top"":null,""offset"":null,""limit"":null,""orderBy"":[],""orderByExpressions"":[],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, c AS payload, [(_FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) ?? -1), (_VectorScore(c.image, [1, 2, 3]) ?? -1.79769e+308)] AS componentScores\nFROM c\nORDER BY _FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) DESC"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":false},{""distinctType"":""None"",""top"":null,""offset"":null,""limit"":null,""orderBy"":[],""orderByExpressions"":[],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, c AS payload, [(_FullTextScore(c.text, [\""swim\"", \""run\""], {documentdb-formattablehybridsearchquery-totaldocumentcount}, {documentdb-formattablehybridsearchquery-totalwordcount-0}, {documentdb-formattablehybridsearchquery-hitcountsarray-0}) ?? -1), (_VectorScore(c.image, [1, 2, 3]) ?? -1.79769e+308)] AS componentScores\nFROM c\nORDER BY _VectorScore(c.image, [1, 2, 3])"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":false}],""componentWithoutPayloadQueryInfos"":[],""projectionQueryInfo"":null,""componentWeights"":[],""skip"":null,""take"":10,""requiresGlobalStatistics"":true}"), MakeTest( query: "SELECT TOP 10 * FROM c ORDER BY RANK RRF(VectorDistance(c.backup_image, [0.5, 0.2, 0.33]), VectorDistance(c.image, [1, 2, 3]), [1, -0.3])", expected: @"{""globalStatisticsQuery"":""SELECT COUNT(1) AS documentCount, [] AS fullTextStatistics\nFROM c"",""componentQueryInfos"":[{""distinctType"":""None"",""top"":120,""offset"":null,""limit"":null,""orderBy"":[""Descending""],""orderByExpressions"":[""_VectorScore(c.backup_image, [0.5, 0.2, 0.33])""],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, [{\""item\"": _VectorScore(c.backup_image, [0.5, 0.2, 0.33])}] AS orderByItems, {\""payload\"": c, \""componentScores\"": [(_VectorScore(c.backup_image, [0.5, 0.2, 0.33]) ?? -1.79769e+308), (_VectorScore(c.image, [1, 2, 3]) ?? 1.79769e+308)]} AS payload\nFROM c\nWHERE ({documentdb-formattableorderbyquery-filter})\nORDER BY _VectorScore(c.backup_image, [0.5, 0.2, 0.33])"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":true},{""distinctType"":""None"",""top"":120,""offset"":null,""limit"":null,""orderBy"":[""Ascending""],""orderByExpressions"":[""_VectorScore(c.image, [1, 2, 3])""],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, [{\""item\"": _VectorScore(c.image, [1, 2, 3])}] AS orderByItems, {\""payload\"": c, \""componentScores\"": [(_VectorScore(c.backup_image, [0.5, 0.2, 0.33]) ?? -1.79769e+308), (_VectorScore(c.image, [1, 2, 3]) ?? 1.79769e+308)]} AS payload\nFROM c\nWHERE ({documentdb-formattableorderbyquery-filter})\nORDER BY _VectorScore(c.image, [1, 2, 3]) ASC"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":true}],""componentWithoutPayloadQueryInfos"":[],""projectionQueryInfo"":null,""componentWeights"":[1.0,0.3],""skip"":null,""take"":10,""requiresGlobalStatistics"":false}"), MakeHybridSearchOptimizationTest( query: "SELECT TOP 10 * FROM c ORDER BY RANK RRF(VectorDistance(c.backup_image, [0.5, 0.2, 0.33]), VectorDistance(c.image, [1, 2, 3]), [1, -0.3])", - expected: @"{""globalStatisticsQuery"":""SELECT COUNT(1) AS documentCount, [] AS fullTextStatistics\nFROM c"",""componentQueryInfos"":[{""distinctType"":""None"",""top"":120,""offset"":null,""limit"":null,""orderBy"":[],""orderByExpressions"":[],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, c AS payload, [(_VectorScore(c.backup_image, [0.5, 0.2, 0.33]) ?? -1.79769e+308), (-1 * (_VectorScore(c.image, [1, 2, 3]) ?? 1.79769e+308))] AS componentScores\nFROM c\nORDER BY _VectorScore(c.backup_image, [0.5, 0.2, 0.33])"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":false},{""distinctType"":""None"",""top"":120,""offset"":null,""limit"":null,""orderBy"":[],""orderByExpressions"":[],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, c AS payload, [(_VectorScore(c.backup_image, [0.5, 0.2, 0.33]) ?? -1.79769e+308), (-1 * (_VectorScore(c.image, [1, 2, 3]) ?? 1.79769e+308))] AS componentScores\nFROM c\nORDER BY _VectorScore(c.image, [1, 2, 3]) ASC"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":false}],""componentWithoutPayloadQueryInfos"":[],""projectionQueryInfo"":null,""componentWeights"":[1.0,0.3],""skip"":null,""take"":10,""requiresGlobalStatistics"":false}"), + expected: @"{""globalStatisticsQuery"":""SELECT COUNT(1) AS documentCount, [] AS fullTextStatistics\nFROM c"",""componentQueryInfos"":[{""distinctType"":""None"",""top"":null,""offset"":null,""limit"":null,""orderBy"":[],""orderByExpressions"":[],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, c AS payload, [(_VectorScore(c.backup_image, [0.5, 0.2, 0.33]) ?? -1.79769e+308), (-1 * (_VectorScore(c.image, [1, 2, 3]) ?? 1.79769e+308))] AS componentScores\nFROM c\nORDER BY _VectorScore(c.backup_image, [0.5, 0.2, 0.33])"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":false},{""distinctType"":""None"",""top"":null,""offset"":null,""limit"":null,""orderBy"":[],""orderByExpressions"":[],""groupByExpressions"":[],""groupByAliases"":[],""aggregates"":[],""groupByAliasToAggregateType"":{},""rewrittenQuery"":""SELECT TOP 120 c._rid, c AS payload, [(_VectorScore(c.backup_image, [0.5, 0.2, 0.33]) ?? -1.79769e+308), (-1 * (_VectorScore(c.image, [1, 2, 3]) ?? 1.79769e+308))] AS componentScores\nFROM c\nORDER BY _VectorScore(c.image, [1, 2, 3]) ASC"",""hasSelectValue"":false,""dCountInfo"":null,""hasNonStreamingOrderBy"":false}],""componentWithoutPayloadQueryInfos"":[],""projectionQueryInfo"":null,""componentWeights"":[1.0,0.3],""skip"":null,""take"":10,""requiresGlobalStatistics"":false}"), }; QueryPartitionProvider queryPartitionProvider = new QueryPartitionProvider(DefaultQueryEngineConfiguration); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/SubpartitionTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/SubpartitionTests.cs index 01648d77d4..ae281a6c3a 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/SubpartitionTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/SubpartitionTests.cs @@ -135,9 +135,10 @@ private static IQueryPipelineStage CreateQueryPipelineStage( properties: new Dictionary() { { "x-ms-query-partitionkey-definition", partitionKeyDefinition } }, partitionedQueryExecutionInfo: null, returnResultsInDeterministicOrder: null, - enableOptimisticDirectExecution: queryRequestOptions.EnableOptimisticDirectExecution, - isHybridSearchQueryPlanOptimizationDisabled: queryRequestOptions.IsHybridSearchQueryPlanOptimizationDisabled, + enableOptimisticDirectExecution: queryRequestOptions.EnableOptimisticDirectExecution, + isHybridSearchQueryPlanOptimizationDisabled: queryRequestOptions.IsHybridSearchQueryPlanOptimizationDisabled, enableDistributedQueryGatewayMode: queryRequestOptions.EnableDistributedQueryGatewayMode, + fullTextScoreScope: queryRequestOptions.FullTextScoreScope, testInjections: queryRequestOptions.TestSettings); List targetPkRanges = new(); @@ -335,7 +336,8 @@ public override Task GetCachedContainerQueryProperties }, SubpartitionTests.CreatePartitionKeyDefinition(), vectorEmbeddingPolicy: null, - Cosmos.GeospatialType.Geometry)); + Cosmos.GeospatialType.Geometry, + false)); } public override Task GetClientDisableOptimisticDirectExecutionAsync() diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/RetryHandlerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/RetryHandlerTests.cs index ca52e5d356..81e2d35388 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/RetryHandlerTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/RetryHandlerTests.cs @@ -141,7 +141,7 @@ private class TestPartitionKeyRangeCache : PartitionKeyRangeCache private readonly IReadOnlyList overlappingRanges; public TestPartitionKeyRangeCache(IReadOnlyList overlappingRanges) - : base(null, null, null, null) // Pass nulls or mocks as needed for base constructor + : base(null, null, null, null, false, false) // Pass nulls or mocks as needed for base constructor { this.overlappingRanges = overlappingRanges; } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/IRoutingMapProviderExtensionsTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/IRoutingMapProviderExtensionsTest.cs index b4629fe063..43922b6284 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/IRoutingMapProviderExtensionsTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/IRoutingMapProviderExtensionsTest.cs @@ -26,7 +26,7 @@ private class MockRoutingMapProvider : IRoutingMapProvider public MockRoutingMapProvider(IList ranges) { - this.routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap(ranges.Select(r => Tuple.Create(r, (ServiceIdentity)null)), ""); + this.routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap(ranges.Select(r => Tuple.Create(r, (ServiceIdentity)null)), "", false); } public Task> TryGetOverlappingRangesAsync( diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/PartitionRoutingHelperTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/PartitionRoutingHelperTest.cs index 032b099a48..83db50eb99 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/PartitionRoutingHelperTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/PartitionRoutingHelperTest.cs @@ -102,7 +102,7 @@ public async Task TestAddFormattedContinuationToHeader() CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap( - testData.RoutingMap.Select(range => Tuple.Create(range, (ServiceIdentity)null)), string.Empty); + testData.RoutingMap.Select(range => Tuple.Create(range, (ServiceIdentity)null)), string.Empty, false); RoutingMapProvider routingMapProvider = new RoutingMapProvider(routingMap); foreach (AddFormattedContinuationToHeaderTestUnit positiveTestData in testData.TestSet.Postive) @@ -221,7 +221,7 @@ public async Task TestGetPartitionRoutingInfo() CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap( - testData.RoutingMap.Select(range => Tuple.Create(range, (ServiceIdentity)null)), string.Empty); + testData.RoutingMap.Select(range => Tuple.Create(range, (ServiceIdentity)null)), string.Empty, false); foreach (GetPartitionRoutingInfoTestCase testCase in testData.TestCases) { @@ -706,7 +706,7 @@ private static async Task PrefixPartitionKeyTestRunnerAsync( CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap( rangesAndServiceIdentity, - collectionRid); + collectionRid, false); RoutingMapProvider routingMapProvider = new RoutingMapProvider(routingMap); TryCatch tryGetQueryPlan = @@ -747,7 +747,7 @@ private static async Task PrefixPartitionKeyChangeFeedTestRunnerAsync( CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap( rangesAndServiceIdentity, - collectionRid); + collectionRid, false); RoutingMapProvider routingMapProvider = new RoutingMapProvider(routingMap); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/SettingsContractTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/SettingsContractTests.cs index 4e7e4a9861..a1909c7697 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/SettingsContractTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/SettingsContractTests.cs @@ -19,6 +19,7 @@ namespace Microsoft.Azure.Cosmos.Tests using Newtonsoft.Json; using Newtonsoft.Json.Linq; using FullTextPath = Microsoft.Azure.Cosmos.FullTextPath; + using FullTextPolicy = Microsoft.Azure.Cosmos.FullTextPolicy; [TestClass] public class SettingsContractTests @@ -340,7 +341,7 @@ public void AccountPropertiesDeserializeWithAdditionalDataTest() [TestMethod] public void ContainerPropertiesDeserializeWithAdditionalDataTest() { - string cosmosSerialized = "{\"indexingPolicy\":{\"automatic\":true,\"indexingMode\":\"Consistent\",\"additionalIndexPolicy\":\"indexpolicyvalue\",\"includedPaths\":[{\"path\":\"/included/path\",\"additionalIncludedPath\":\"includedPathValue\",\"indexes\":[]}],\"excludedPaths\":[{\"path\":\"/excluded/path\",\"additionalExcludedPath\":\"excludedPathValue\"}],\"compositeIndexes\":[[{\"path\":\"/composite/path\",\"additionalCompositeIndex\":\"compositeIndexValue\",\"order\":\"ascending\"}]],\"spatialIndexes\":[{\"path\":\"/spatial/path\",\"additionalSpatialIndexes\":\"spatialIndexValue\",\"types\":[]}],\"vectorIndexes\":[{\"path\":\"/vector1\",\"type\":\"flat\",\"additionalVectorIndex\":\"vectorIndexValue1\"},{\"path\":\"/vector2\",\"type\":\"quantizedFlat\",\"additionalVectorIndex\":\"vectorIndexValue2\"},{\"path\":\"/vector3\",\"type\":\"diskANN\"}],\"fullTextIndexes\":[{\"path\":\"/fullTextPath1\",\"additionalFullTextIndex\":\"fullTextIndexValue1\"},{\"path\":\"/fullTextPath2\",\"additionalFullTextIndex\":\"fullTextIndexValue2\"},{\"path\":\"/fullTextPath3\"}]},\"computedProperties\":[{\"name\":\"lowerName\",\"query\":\"SELECT VALUE LOWER(c.name) FROM c\"},{\"name\":\"estimatedTax\",\"query\":\"SELECT VALUE c.salary * 0.2 FROM c\"}],\"geospatialConfig\":{\"type\":\"Geography\",\"additionalGeospatialConfig\":\"geospatialConfigValue\"},\"uniqueKeyPolicy\":{\"additionalUniqueKeyPolicy\":\"uniqueKeyPolicyValue\",\"uniqueKeys\":[{\"paths\":[\"/unique/key/path/1\",\"/unique/key/path/2\"]}]},\"conflictResolutionPolicy\":{\"mode\":\"LastWriterWins\",\"additionalConflictResolutionPolicy\":\"conflictResolutionValue\"},\"clientEncryptionPolicy\":{\"includedPaths\":[{\"path\":\"/path\",\"clientEncryptionKeyId\":\"clientEncryptionKeyId\",\"encryptionType\":\"Randomized\",\"additionalIncludedPath\":\"includedPathValue\",\"encryptionAlgorithm\":\"AEAD_AES_256_CBC_HMAC_SHA256\"}],\"policyFormatVersion\":1,\"additionalEncryptionPolicy\":\"clientEncryptionpolicyValue\"},\"id\":\"2a9f501b-6948-4795-8fd1-797defb5c466\",\"partitionKey\":{\"paths\":[],\"kind\":\"Hash\"},\"vectorEmbeddingPolicy\":{\"vectorEmbeddings\":[{\"path\":\"/vector1\",\"dataType\":\"float32\",\"dimensions\":1200,\"distanceFunction\":\"cosine\"},{\"path\":\"/vector2\",\"dataType\":\"int8\",\"dimensions\":3,\"distanceFunction\":\"dotproduct\"},{\"path\":\"/vector3\",\"dataType\":\"uint8\",\"dimensions\":400,\"distanceFunction\":\"euclidean\"}]},\"fullTextPolicy\": {\"defaultLanguage\": \"en-US\",\"fullTextPaths\": [{\"path\": \"/fullTextPath1\",\"language\": \"en-US\"},{\"path\": \"/fullTextPath2\",\"language\": \"en-US\"},{\"path\": \"/fullTextPath3\",\"language\": \"en-US\"}]}}"; + string cosmosSerialized = "{\"indexingPolicy\":{\"automatic\":true,\"indexingMode\":\"Consistent\",\"additionalIndexPolicy\":\"indexpolicyvalue\",\"includedPaths\":[{\"path\":\"/included/path\",\"additionalIncludedPath\":\"includedPathValue\",\"indexes\":[]}],\"excludedPaths\":[{\"path\":\"/excluded/path\",\"additionalExcludedPath\":\"excludedPathValue\"}],\"compositeIndexes\":[[{\"path\":\"/composite/path\",\"additionalCompositeIndex\":\"compositeIndexValue\",\"order\":\"ascending\"}]],\"spatialIndexes\":[{\"path\":\"/spatial/path\",\"additionalSpatialIndexes\":\"spatialIndexValue\",\"types\":[]}],\"vectorIndexes\":[{\"path\":\"/vector1\",\"type\":\"flat\",\"additionalVectorIndex\":\"vectorIndexValue1\"},{\"path\":\"/vector2\",\"type\":\"quantizedFlat\",\"additionalVectorIndex\":\"vectorIndexValue2\"},{\"path\":\"/vector3\",\"type\":\"diskANN\"}],\"fullTextIndexes\":[{\"path\":\"/fullTextPath1\",\"additionalFullTextIndex\":\"fullTextIndexValue1\"},{\"path\":\"/fullTextPath2\",\"additionalFullTextIndex\":\"fullTextIndexValue2\"},{\"path\":\"/fullTextPath3\"}]},\"computedProperties\":[{\"name\":\"lowerName\",\"query\":\"SELECT VALUE LOWER(c.name) FROM c\"},{\"name\":\"estimatedTax\",\"query\":\"SELECT VALUE c.salary * 0.2 FROM c\"}],\"geospatialConfig\":{\"type\":\"Geography\",\"additionalGeospatialConfig\":\"geospatialConfigValue\"},\"uniqueKeyPolicy\":{\"additionalUniqueKeyPolicy\":\"uniqueKeyPolicyValue\",\"uniqueKeys\":[{\"paths\":[\"/unique/key/path/1\",\"/unique/key/path/2\"]}]},\"conflictResolutionPolicy\":{\"mode\":\"LastWriterWins\",\"additionalConflictResolutionPolicy\":\"conflictResolutionValue\"},\"clientEncryptionPolicy\":{\"includedPaths\":[{\"path\":\"/path\",\"clientEncryptionKeyId\":\"clientEncryptionKeyId\",\"encryptionType\":\"Randomized\",\"additionalIncludedPath\":\"includedPathValue\",\"encryptionAlgorithm\":\"AEAD_AES_256_CBC_HMAC_SHA256\"}],\"policyFormatVersion\":1,\"additionalEncryptionPolicy\":\"clientEncryptionpolicyValue\"},\"id\":\"2a9f501b-6948-4795-8fd1-797defb5c466\",\"partitionKey\":{\"paths\":[],\"kind\":\"Hash\"},\"vectorEmbeddingPolicy\":{\"vectorEmbeddings\":[{\"path\":\"/vector1\",\"dataType\":\"float32\",\"dimensions\":1200,\"distanceFunction\":\"cosine\"},{\"path\":\"/vector2\",\"dataType\":\"int8\",\"dimensions\":3,\"distanceFunction\":\"dotproduct\"},{\"path\":\"/vector3\",\"dataType\":\"uint8\",\"dimensions\":400,\"distanceFunction\":\"euclidean\"},{\"path\":\"/vector4\",\"dataType\":\"float16\",\"dimensions\":3,\"distanceFunction\":\"dotproduct\"}]},\"fullTextPolicy\": {\"defaultLanguage\": \"en-US\",\"fullTextPaths\": [{\"path\": \"/fullTextPath1\",\"language\": \"en-US\"},{\"path\": \"/fullTextPath2\",\"language\": \"en-US\"},{\"path\": \"/fullTextPath3\",\"language\": \"en-US\"}]}}"; JObject complexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname" } }); @@ -405,12 +406,28 @@ public void ContainerPropertiesDeserializeWithAdditionalDataTest() Assert.AreEqual("includedPathValue", containerProperties.ClientEncryptionPolicy.IncludedPaths.First().AdditionalProperties["additionalIncludedPath"]); Assert.IsNotNull(containerProperties.VectorEmbeddingPolicy); - Assert.AreEqual(3, containerProperties.VectorEmbeddingPolicy.Embeddings.Count); + Assert.AreEqual(4, containerProperties.VectorEmbeddingPolicy.Embeddings.Count); + Assert.AreEqual("/vector1", containerProperties.VectorEmbeddingPolicy.Embeddings[0].Path); Assert.AreEqual(Cosmos.VectorDataType.Float32, containerProperties.VectorEmbeddingPolicy.Embeddings[0].DataType); Assert.AreEqual(1200, containerProperties.VectorEmbeddingPolicy.Embeddings[0].Dimensions); Assert.AreEqual(Cosmos.DistanceFunction.Cosine, containerProperties.VectorEmbeddingPolicy.Embeddings[0].DistanceFunction); + Assert.AreEqual("/vector2", containerProperties.VectorEmbeddingPolicy.Embeddings[1].Path); + Assert.AreEqual(Cosmos.VectorDataType.Int8, containerProperties.VectorEmbeddingPolicy.Embeddings[1].DataType); + Assert.AreEqual(3, containerProperties.VectorEmbeddingPolicy.Embeddings[1].Dimensions); + Assert.AreEqual(Cosmos.DistanceFunction.DotProduct, containerProperties.VectorEmbeddingPolicy.Embeddings[1].DistanceFunction); + + Assert.AreEqual("/vector3", containerProperties.VectorEmbeddingPolicy.Embeddings[2].Path); + Assert.AreEqual(Cosmos.VectorDataType.Uint8, containerProperties.VectorEmbeddingPolicy.Embeddings[2].DataType); + Assert.AreEqual(400, containerProperties.VectorEmbeddingPolicy.Embeddings[2].Dimensions); + Assert.AreEqual(Cosmos.DistanceFunction.Euclidean, containerProperties.VectorEmbeddingPolicy.Embeddings[2].DistanceFunction); + + Assert.AreEqual("/vector4", containerProperties.VectorEmbeddingPolicy.Embeddings[3].Path); + Assert.AreEqual(Cosmos.VectorDataType.Float16, containerProperties.VectorEmbeddingPolicy.Embeddings[3].DataType); + Assert.AreEqual(3, containerProperties.VectorEmbeddingPolicy.Embeddings[3].Dimensions); + Assert.AreEqual(Cosmos.DistanceFunction.DotProduct, containerProperties.VectorEmbeddingPolicy.Embeddings[3].DistanceFunction); + Assert.IsNotNull(containerProperties.FullTextPolicy); Assert.AreEqual("en-US", containerProperties.FullTextPolicy.DefaultLanguage); Assert.AreEqual(3, containerProperties.FullTextPolicy.FullTextPaths.Count); @@ -1189,6 +1206,71 @@ public void FullTextPolicySerialization() Assert.IsTrue(fullTextPath2.Equals(fullTextPathsDeSerialized.Value()[1].ToObject())); } + [TestMethod] + [DataRow("en-US")] + [DataRow("fr-FR")] + [DataRow("de-DE")] + [DataRow("it-IT")] + [DataRow("pt-BR")] + [DataRow("pt-PT")] + [DataRow("es-ES")] + public void FullTextPolicySerializationWithAllSupportedLanguages(string language) + { + FullTextPolicy fullTextPolicy = new FullTextPolicy + { + DefaultLanguage = language, + FullTextPaths = new Collection + { + new FullTextPath { Path = "/text1", Language = language }, + new FullTextPath { Path = "/text2", Language = "en-US" }, + new FullTextPath { Path = "/text3" } // No language specified, should use default + } + }; + + string serialized = CosmosSerialize(fullTextPolicy); + Assert.IsNotNull(serialized); + Assert.IsTrue(serialized.Contains($"\"defaultLanguage\":\"{language}\""), + $"Serialized JSON should contain defaultLanguage: {language}"); + + FullTextPolicy deserialized = CosmosDeserialize(serialized); + Assert.IsNotNull(deserialized); + Assert.AreEqual(language, deserialized.DefaultLanguage, + $"DefaultLanguage mismatch after deserialization for: {language}"); + Assert.AreEqual(3, deserialized.FullTextPaths.Count); + Assert.AreEqual(language, deserialized.FullTextPaths[0].Language); + Assert.AreEqual("en-US", deserialized.FullTextPaths[1].Language); + Assert.IsNull(deserialized.FullTextPaths[2].Language); + } + + [TestMethod] + [DataRow("en-US")] + [DataRow("fr-FR")] + [DataRow("de-DE")] + [DataRow("it-IT")] + [DataRow("ja-JP")] + [DataRow("pt-BR")] + [DataRow("pt-PT")] + [DataRow("es-ES")] + public void FullTextPathSerializationWithAllLanguages(string language) + { + FullTextPath fullTextPath = new FullTextPath + { + Path = "/testPath", + Language = language + }; + + string serialized = CosmosSerialize(fullTextPath); + Assert.IsNotNull(serialized); + Assert.IsTrue(serialized.Contains($"\"language\":\"{language}\""), + $"Serialized JSON should contain language: {language}"); + + FullTextPath deserialized = CosmosDeserialize(serialized); + Assert.IsNotNull(deserialized); + Assert.AreEqual("/testPath", deserialized.Path); + Assert.AreEqual(language, deserialized.Language, + $"Language mismatch after deserialization for: {language}"); + } + private static T CosmosDeserialize(string payload) { using (MemoryStream ms = new MemoryStream()) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/StoreReaderTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/StoreReaderTest.cs index cfca49d25b..b603bd2e3d 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/StoreReaderTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/StoreReaderTest.cs @@ -8,6 +8,7 @@ namespace Microsoft.Azure.Cosmos using System.Globalization; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.ChangeFeed.DocDBErrors; using Microsoft.Azure.Cosmos.Common; using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Documents; @@ -599,13 +600,13 @@ public void GlobalStrongConsistentWriteMockTest() { TransportClient mockTransportClient = this.GetMockTransportClientForGlobalStrongWrites(addressInformation, i, false, false, false); StoreReader storeReader = new StoreReader(mockTransportClient, addressSelector, new AddressEnumerator(), sessionContainer, false); - ConsistencyWriter consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: null); + ConsistencyWriter consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); StoreResponse response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(30)), false).Result; Assert.AreEqual(100, response.LSN); //globalCommittedLsn never catches up in this case mockTransportClient = this.GetMockTransportClientForGlobalStrongWrites(addressInformation, i, true, false, false); - consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: null); + consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); try { response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(30)), false).Result; @@ -616,17 +617,17 @@ public void GlobalStrongConsistentWriteMockTest() } mockTransportClient = this.GetMockTransportClientForGlobalStrongWrites(addressInformation, i, false, true, false); - consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: null); + consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(30)), false).Result; Assert.AreEqual(100, response.LSN); mockTransportClient = this.GetMockTransportClientForGlobalStrongWrites(addressInformation, i, false, true, true); - consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: null); + consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(30)), false).Result; Assert.AreEqual(100, response.LSN); mockTransportClient = this.GetMockTransportClientForGlobalStrongWrites(addressInformation, i, false, false, true); - consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: null); + consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(30)), false).Result; Assert.AreEqual(100, response.LSN); } @@ -819,5 +820,179 @@ public void GlobalStrongConsistencyMockTest() Assert.IsTrue(nGlobalCommitedLSN == 90); } } + + private TransportClient GetMockTransportClientForNRegionSynchronousWrites( + AddressInformation[] addressInformation, bool globalNLsnNeverCatchesUp) + { + Mock mockTransportClient = new Mock(); + + // create mock store response object + StoreResponse mockStoreResponse1 = new StoreResponse(); + StoreResponse mockStoreResponse2 = new StoreResponse(); + StoreResponse mockStoreResponse3 = new StoreResponse(); + StoreResponse mockStoreResponse4 = new StoreResponse(); + StoreResponse mockStoreResponse5 = new StoreResponse(); + + + // set lsn and activityid on the store response. + mockStoreResponse1.Headers = new StoreResponseNameValueCollection() + { + { WFConstants.BackendHeaders.LSN, "100"}, + { WFConstants.BackendHeaders.ActivityId, "ACTIVITYID1_1" }, + { WFConstants.BackendHeaders.GlobalNRegionCommittedGLSN, "90" }, + { WFConstants.BackendHeaders.NumberOfReadRegions, "1" }, + }; + + mockStoreResponse2.Headers = new StoreResponseNameValueCollection() + { + { WFConstants.BackendHeaders.LSN, "100"}, + { WFConstants.BackendHeaders.ActivityId, "ACTIVITYID1_2" }, + { WFConstants.BackendHeaders.GlobalNRegionCommittedGLSN, "95" }, + { WFConstants.BackendHeaders.NumberOfReadRegions, "1" }, + }; + + mockStoreResponse3.Headers = new StoreResponseNameValueCollection() + { + { WFConstants.BackendHeaders.LSN, "103"}, + { WFConstants.BackendHeaders.ActivityId, "ACTIVITYID1_3" }, + { WFConstants.BackendHeaders.GlobalNRegionCommittedGLSN, "98" }, + { WFConstants.BackendHeaders.NumberOfReadRegions, "1" }, + }; + + mockStoreResponse4.Headers = new StoreResponseNameValueCollection() + { + { WFConstants.BackendHeaders.LSN, "103"}, + { WFConstants.BackendHeaders.ActivityId, "ACTIVITYID1_3" }, + { WFConstants.BackendHeaders.GlobalNRegionCommittedGLSN, "99" }, + { WFConstants.BackendHeaders.NumberOfReadRegions, "1" }, + }; + + mockStoreResponse5.Headers = new StoreResponseNameValueCollection() + { + { WFConstants.BackendHeaders.LSN, "106"}, + { WFConstants.BackendHeaders.ActivityId, "ACTIVITYID1_3" }, + { WFConstants.BackendHeaders.GlobalNRegionCommittedGLSN, "100" }, + { WFConstants.BackendHeaders.NumberOfReadRegions, "1" }, + }; + + for (int i = 0; i < addressInformation.Length; i++) + { + + if (globalNLsnNeverCatchesUp) + { + mockTransportClient.Setup(client => client.InvokeResourceOperationAsync( + new TransportAddressUri(new Uri(addressInformation[i].PhysicalUri)), It.IsAny())) + .Returns(Task.FromResult(mockStoreResponse1)); + + } + else + { + mockTransportClient.SetupSequence(client => client.InvokeResourceOperationAsync( + new TransportAddressUri(new Uri(addressInformation[i].PhysicalUri)), It.IsAny())) + .Returns(Task.FromResult(mockStoreResponse1)) // initial write response + .Returns(Task.FromResult(mockStoreResponse2)) // barrier retry, count 1 + .Returns(Task.FromResult(mockStoreResponse3)) // barrier retry, count 2 + .Returns(Task.FromResult(mockStoreResponse4)) // barrier retry, count 3 + .Returns(Task.FromResult(mockStoreResponse5)); // barrier retry, count 4 GlobalNRegionCommittedGLSN catches up. + } + + } + + return mockTransportClient.Object; + } + + /** + + Tests the feature called nregion synchronous commit. This is an account level feature enabled via the accountConfigProperties with property name "EnableNRegionSynchronousCommit" + + Business logic: We send single request to primary of the Write region,which will take care of replicating to its secondaries, one of which is XPPrimary. XPPrimary in this case will replicate this request to n read regions, which will ack from within their region. + In the write region where the original request was sent to , the request returns from the backend once write quorum number of replicas commits the write - but at this time, the response cannot be returned to caller, since linearizability guarantees will be violated. + ConsistencyWriter will continuously issue barrier head requests against the partition in question, until GlobalNRegionCommittedGLSN is at least as big as the lsn of the original response. + Sequence of steps: + 1. After receiving response from primary of write region, look at GlobalNRegionCommittedGLSN and LSN headers. + 2. If GlobalNRegionCommittedGLSN == LSN, return response to caller + 3. If GlobalNRegionCommittedGLSN < LSN && storeResponse.NumberOfReadRegions > 0 , cache LSN in request as SelectedGlobalNRegionCommittedGLSN, and issue barrier requests against any/all replicas. + 4. Each barrier response will contain its own LSN and GlobalNRegionCommittedGLSN, check for any response that satisfies GlobalNRegionCommittedGLSN >= SelectedGlobalNRegionCommittedGLSN + 5. Return to caller on success. + + **/ + [TestMethod] + public void TestWhenNRegionSynchronousCommitEnabledThenDoBarrierHead() + { + // create a real document service request (with auth token level = god) + DocumentServiceRequest entity = DocumentServiceRequest.Create(OperationType.Create, ResourceType.Document, AuthorizationTokenType.SystemAll); + + // set request charge tracker - this is referenced in store reader (ReadMultipleReplicaAsync) + DocumentServiceRequestContext requestContext = new DocumentServiceRequestContext + { + RequestChargeTracker = new RequestChargeTracker() + }; + entity.RequestContext = requestContext; + + // set a dummy resource id on the request. + entity.ResourceId = "1-MxAPlgMgA="; + + // set consistency level on the request to Bounded Staleness + entity.Headers[HttpConstants.HttpHeaders.ConsistencyLevel] = ConsistencyLevel.Session.ToString(); + + // also setup timeout helper, used in store reader + entity.RequestContext.TimeoutHelper = new TimeoutHelper(new TimeSpan(2, 2, 2)); + + // when the store reader throws Invalid Partition exception, the higher layer should + // clear this target identity. + entity.RequestContext.TargetIdentity = new ServiceIdentity("dummyTargetIdentity1", new Uri("http://dummyTargetIdentity1"), false); + entity.RequestContext.ResolvedPartitionKeyRange = new PartitionKeyRange(); + + AddressInformation[] addressInformation = this.GetMockAddressInformationDuringUpgrade(); + Mock mockAddressCache = this.GetMockAddressCache(addressInformation); + + // validate that the mock works + PartitionAddressInformation partitionAddressInformation = mockAddressCache.Object.ResolveAsync(entity, false, new CancellationToken()).Result; + IReadOnlyList addressInfo = partitionAddressInformation.AllAddresses; + + Assert.IsTrue(addressInfo[0] == addressInformation[0]); + + AddressSelector addressSelector = new AddressSelector(mockAddressCache.Object, Protocol.Tcp); + Uri primaryAddress = addressSelector.ResolvePrimaryTransportAddressUriAsync(entity, false /*forceAddressRefresh*/).Result.Uri; + + // check if the address return from Address Selector matches the original address info + Assert.IsTrue(primaryAddress.Equals(addressInformation[0].PhysicalUri)); + + ISessionContainer sessionContainer = new SessionContainer(string.Empty); + + Mock mockServiceConfigReader = new Mock(); + mockServiceConfigReader.Setup(reader => reader.DefaultConsistencyLevel).Returns(Documents.ConsistencyLevel.Session); + + Mock mockAuthorizationTokenProvider = new Mock(); + mockAuthorizationTokenProvider.Setup(provider => provider.AddSystemAuthorizationHeaderAsync( + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(0)); + + TransportClient mockTransportClient = this.GetMockTransportClientForNRegionSynchronousWrites(addressInformation, false); + StoreReader storeReader = new StoreReader(mockTransportClient, addressSelector, new AddressEnumerator(), sessionContainer, false); + + ConsistencyWriter consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); + StoreResponse response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(3000)), false).Result; + Assert.AreEqual(100, response.LSN); + + + try + { + mockTransportClient = this.GetMockTransportClientForNRegionSynchronousWrites(addressInformation, true); + storeReader = new StoreReader(mockTransportClient, addressSelector, new AddressEnumerator(), sessionContainer, false); + + consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); + response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(3000)), false).Result; + Assert.Fail(); + } + catch (AggregateException ex) + { + if (ex.InnerException is DocumentClientException goneEx) + { + DefaultTrace.TraceInformation("Gone exception expected!"); + Assert.AreEqual(SubStatusCodes.Server_NRegionCommitWriteBarrierNotMet, goneEx.GetSubStatusCode()); + } + } + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/OpenTelemetryRecorderTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/OpenTelemetryRecorderTests.cs index 471c91765a..9640097210 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/OpenTelemetryRecorderTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/OpenTelemetryRecorderTests.cs @@ -62,7 +62,8 @@ public async Task CheckResponseCompatibility() "MediaResponse", // Part of dead code "DocumentFeedResponse`1",// Part of dead code "CosmosQuotaResponse",// Part of dead code - "StoredProcedureResponse`1" // Not supported as of now + "StoredProcedureResponse`1", // Not supported as of now + "DistributedTransactionResponse" // // Distributed transaction response in internal as of now }; // This dictionary contains a Key-Value pair where the Key represents the Response Type compatible with Open Telemetry Response, and the corresponding Value is a mocked instance. @@ -96,13 +97,11 @@ public async Task CheckResponseCompatibility() IEnumerable actualClasses = asm .GetTypes() .Where(type => - (type.Name.EndsWith("Response") || type.Name.EndsWith("Response`1")) && // Ending with Response and Response - !type.Name.Contains("OpenTelemetryResponse") && // Excluding OpenTelemetryResponse because we are testing this class - !type.IsAbstract && // Excluding abstract classes - !type.IsInterface && // Excluding interfaces - !excludedResponses.Contains(type.Name) && // Excluding all the types defined in excludedResponses list - !(type.Namespace != null && type.Namespace.StartsWith("Microsoft.Azure.Documents")) // Exclude types from direct Microsoft.Azure.Documents - ); + (type.Name.EndsWith("Response") || type.Name.EndsWith("Response`1")) && // Ending with Response and Response + !type.Name.Contains("OpenTelemetryResponse") && // Excluding OpenTelemetryResponse because we are testing this class + !type.IsAbstract && // Excluding abstract classes + !type.IsInterface && // Excluding interfaces + !excludedResponses.Contains(type.Name)); // Excluding all the types defined in excludedResponses list foreach (Type className in actualClasses) { @@ -330,4 +329,4 @@ internal NewCosmosObjectDisposedException(ObjectDisposedException originalExcept public override string Message => "dummy CosmosObjectDisposedException message"; } -} \ No newline at end of file +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreClientTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreClientTests.cs index 8623ca4290..7e400450a8 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreClientTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreClientTests.cs @@ -61,7 +61,8 @@ public async Task InvokeAsync_ShouldThrowDocumentClientException(HttpStatusCode httpClient: cosmosHttpClient, eventSource: null, userAgentContainer: userAgentContainer, - serializerSettings: null); + serializerSettings: null, + globalPartitionEndpointManager: GlobalPartitionEndpointManagerNoOp.Instance); DocumentServiceRequest request = DocumentServiceRequest.Create( operationType: OperationType.Read, @@ -125,7 +126,8 @@ public async Task InvokeAsync_Rntbd200_ShouldReturnDocumentServiceResponse() httpClient: cosmosHttpClient, eventSource: null, userAgentContainer: userAgentContainer, - serializerSettings: null); + serializerSettings: null, + globalPartitionEndpointManager: GlobalPartitionEndpointManagerNoOp.Instance); DocumentServiceRequest request = DocumentServiceRequest.Create( operationType: OperationType.Read, @@ -203,7 +205,8 @@ public async Task InvokeAsync_ShouldOnlyAddUserAgentAndActivityIdHeadersToProxyR httpClient: mockCosmosHttpClient.Object, eventSource: null, userAgentContainer: userAgentContainer, - serializerSettings: null); + serializerSettings: null, + globalPartitionEndpointManager: GlobalPartitionEndpointManagerNoOp.Instance); DocumentServiceRequest request = DocumentServiceRequest.Create( operationType: OperationType.Read, @@ -306,7 +309,8 @@ public async Task InvokeAsync_ShouldNotAddProxyEpkHeaders_WhenPartitionKeyRangeI httpClient: mockCosmosHttpClient.Object, eventSource: null, userAgentContainer: userAgentContainer, - serializerSettings: null); + serializerSettings: null, + globalPartitionEndpointManager: GlobalPartitionEndpointManagerNoOp.Instance); DocumentServiceRequest request = DocumentServiceRequest.Create( operationType: OperationType.Read, @@ -366,6 +370,27 @@ await thinClientStoreClient.InvokeAsync( Assert.IsFalse(headers.ContainsKey(ThinClientConstants.ProxyEndEpk), "ProxyEndEpk should not be added when PKRange is null"); } + [TestMethod] + public void Constructor_ShouldThrowArgumentNullException_WhenUserAgentContainerIsNull() + { + // Arrange + Mock mockHttpClient = new Mock(); + ICommunicationEventSource mockEventSource = Mock.Of(); + + // Act & Assert + ArgumentNullException ex = Assert.ThrowsException(() => + new ThinClientStoreClient( + httpClient: mockHttpClient.Object, + userAgentContainer: null, + eventSource: mockEventSource, + globalPartitionEndpointManager: GlobalPartitionEndpointManagerNoOp.Instance, + serializerSettings: null) + ); + + Assert.AreEqual("userAgentContainer", ex.ParamName); + StringAssert.Contains(ex.Message, "UserAgentContainer cannot be null"); + } + private ContainerProperties GetMockContainerProperties() { ContainerProperties containerProperties = new ContainerProperties diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs index b957d05a79..8efe0a0002 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs @@ -4,10 +4,10 @@ namespace Microsoft.Azure.Cosmos.Tests { - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; @@ -15,8 +15,8 @@ namespace Microsoft.Azure.Cosmos.Tests using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Common; - using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -48,7 +48,7 @@ public void TestInitialize() this.endpointManager = new GlobalEndpointManager( owner: mockDocumentClient.Object, connectionPolicy: connectionPolicy); - + Cosmos.UserAgentContainer userAgentContainer = new Microsoft.Azure.Cosmos.UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); this.thinClientStoreModel = new ThinClientStoreModel( endpointManager: this.endpointManager, @@ -57,7 +57,7 @@ public void TestInitialize() defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null, + httpClient: null, userAgentContainer: userAgentContainer); PartitionKeyRangeCache pkRangeCache = @@ -76,7 +76,7 @@ public void TestCleanup() this.thinClientStoreModel?.Dispose(); } - [TestMethod] + [TestMethod] public async Task ProcessMessageAsync_Success_ShouldReturnDocumentServiceResponse() { // Arrange @@ -127,7 +127,7 @@ public async Task ProcessMessageAsync_Success_ShouldReturnDocumentServiceRespons defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null, + httpClient: null, userAgentContainer: userAgentContainer); ClientCollectionCache clientCollectionCache = new Mock( @@ -201,7 +201,7 @@ public async Task ProcessMessageAsync_WithUnsupportedOperations_ShouldFallbackTo }; GlobalEndpointManager multiEndpointMgr = new GlobalEndpointManager(docClientMulti.Object, policy); - + Cosmos.UserAgentContainer userAgentContainer = new Microsoft.Azure.Cosmos.UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); ThinClientStoreModel storeModel = new ThinClientStoreModel( endpointManager: multiEndpointMgr, @@ -210,7 +210,7 @@ public async Task ProcessMessageAsync_WithUnsupportedOperations_ShouldFallbackTo defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: mockCosmosHttpClient.Object, + httpClient: mockCosmosHttpClient.Object, userAgentContainer: userAgentContainer); ClientCollectionCache clientCollectionCache = new Mock( @@ -258,7 +258,7 @@ public void Dispose_ShouldDisposeThinClientStoreClient() [TestMethod] public async Task ProcessMessageAsync_404_ShouldThrowDocumentClientException() - { + { // Arrange MockThinClientStoreClient thinClientStoreClient = new MockThinClientStoreClient( (request, resourceType, uri, endpoint, globalDatabaseAccountName, clientCollectionCache, cancellationToken) => @@ -302,7 +302,7 @@ public async Task ProcessMessageAsync_404_ShouldThrowDocumentClientException() defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null, + httpClient: null, userAgentContainer: userAgentContainer); ClientCollectionCache clientCollectionCache = new Mock( @@ -322,110 +322,110 @@ public async Task ProcessMessageAsync_404_ShouldThrowDocumentClientException() storeModel.SetCaches(partitionKeyRangeCache, clientCollectionCache); - ReplaceThinClientStoreClientField(storeModel, thinClientStoreClient); - - // Act & Assert + ReplaceThinClientStoreClientField(storeModel, thinClientStoreClient); + + // Act & Assert await Assert.ThrowsExceptionAsync( async () => await storeModel.ProcessMessageAsync(request), "Expected 404 DocumentClientException from the final thinClientStore call"); - } - - [TestMethod] - public async Task PartitionLevelFailoverEnabled_ResolvesPartitionKeyRangeAndCallsLocationOverride() - { - Mock mockDocumentClient = new Mock(); - mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); - mockDocumentClient - .Setup(c => c.GetDatabaseAccountInternalAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new AccountProperties()); - - ConnectionPolicy connectionPolicy = new ConnectionPolicy(); - GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, connectionPolicy); - - Mock globalPartitionEndpointManager = new Mock(); - globalPartitionEndpointManager - .Setup(m => m.TryAddPartitionLevelLocationOverride(It.IsAny())) - .Returns(true) - .Verifiable(); - - ISessionContainer sessionContainer = new Mock().Object; - DocumentClientEventSource eventSource = new Mock().Object; - Newtonsoft.Json.JsonSerializerSettings serializerSettings = new Newtonsoft.Json.JsonSerializerSettings(); - CosmosHttpClient httpClient = new Mock().Object; - Cosmos.UserAgentContainer userAgentContainer = new Microsoft.Azure.Cosmos.UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); - - ThinClientStoreModel storeModel = new ThinClientStoreModel( - endpointManager, - globalPartitionEndpointManager.Object, - sessionContainer, - Cosmos.ConsistencyLevel.Session, - eventSource, - serializerSettings, - httpClient, - userAgentContainer, - isPartitionLevelFailoverEnabled: true); - - Mock mockCollectionCache = new Mock( - sessionContainer, - storeModel, - null, - null, - null, - false); - - ContainerProperties containerProperties = new ContainerProperties("test", "/pk"); - typeof(ContainerProperties) - .GetProperty("ResourceId", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public) - ?.SetValue(containerProperties, "testCollectionRid"); - containerProperties.PartitionKeyPath = "/pk"; - - mockCollectionCache - .Setup(c => c.ResolveCollectionAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(containerProperties); - - Mock mockPartitionKeyRangeCache = new Mock( - null, - storeModel, - mockCollectionCache.Object, - endpointManager, - false); - - PartitionKeyRange pkRange = new PartitionKeyRange { Id = "0", MinInclusive = "", MaxExclusive = "FF" }; - List pkRanges = new List { pkRange }; - IEnumerable> rangeTuples = pkRanges.Select(r => Tuple.Create(r, (ServiceIdentity)null)); - CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap(rangeTuples, "testCollectionRid"); - - mockPartitionKeyRangeCache - .Setup(c => c.TryLookupAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(routingMap); - - storeModel.SetCaches(mockPartitionKeyRangeCache.Object, mockCollectionCache.Object); - - DocumentServiceRequest request = CreatePartitionedDocumentRequest(); - - MockThinClientStoreClient mockThinClientStoreClient = new MockThinClientStoreClient( - (DocumentServiceRequest req, ResourceType resourceType, Uri uri, Uri endpoint, string globalDatabaseAccountName, ClientCollectionCache clientCollectionCache, CancellationToken cancellationToken) => - { - MemoryStream stream = new MemoryStream(new byte[] { 1, 2, 3 }); - INameValueCollection headers = new StoreResponseNameValueCollection(); - return Task.FromResult(new DocumentServiceResponse(stream, headers, HttpStatusCode.OK)); - }); - - ReplaceThinClientStoreClientField(storeModel, mockThinClientStoreClient); - - // Act - await storeModel.ProcessMessageAsync(request); - - // Assert - globalPartitionEndpointManager.Verify(m => m.TryAddPartitionLevelLocationOverride(It.IsAny()), Times.Once()); + } + + [TestMethod] + public async Task PartitionLevelFailoverEnabled_ResolvesPartitionKeyRangeAndCallsLocationOverride() + { + Mock mockDocumentClient = new Mock(); + mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); + mockDocumentClient + .Setup(c => c.GetDatabaseAccountInternalAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new AccountProperties()); + + ConnectionPolicy connectionPolicy = new ConnectionPolicy(); + GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, connectionPolicy); + + Mock globalPartitionEndpointManager = new Mock(); + globalPartitionEndpointManager + .Setup(m => m.TryAddPartitionLevelLocationOverride(It.IsAny())) + .Returns(true) + .Verifiable(); + + ISessionContainer sessionContainer = new Mock().Object; + DocumentClientEventSource eventSource = new Mock().Object; + Newtonsoft.Json.JsonSerializerSettings serializerSettings = new Newtonsoft.Json.JsonSerializerSettings(); + CosmosHttpClient httpClient = new Mock().Object; + Cosmos.UserAgentContainer userAgentContainer = new Microsoft.Azure.Cosmos.UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); + + ThinClientStoreModel storeModel = new ThinClientStoreModel( + endpointManager, + globalPartitionEndpointManager.Object, + sessionContainer, + Cosmos.ConsistencyLevel.Session, + eventSource, + serializerSettings, + httpClient, + userAgentContainer, + isPartitionLevelFailoverEnabled: true); + + Mock mockCollectionCache = new Mock( + sessionContainer, + storeModel, + null, + null, + null, + false); + + ContainerProperties containerProperties = new ContainerProperties("test", "/pk"); + typeof(ContainerProperties) + .GetProperty("ResourceId", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public) + ?.SetValue(containerProperties, "testCollectionRid"); + containerProperties.PartitionKeyPath = "/pk"; + + mockCollectionCache + .Setup(c => c.ResolveCollectionAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(containerProperties); + + Mock mockPartitionKeyRangeCache = new Mock( + null, + storeModel, + mockCollectionCache.Object, + endpointManager, + false); + + PartitionKeyRange pkRange = new PartitionKeyRange { Id = "0", MinInclusive = "", MaxExclusive = "FF" }; + List pkRanges = new List { pkRange }; + IEnumerable> rangeTuples = pkRanges.Select(r => Tuple.Create(r, (ServiceIdentity)null)); + CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap(rangeTuples, "testCollectionRid", useLengthAwareRangeComparer: false); + + mockPartitionKeyRangeCache + .Setup(c => c.TryLookupAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(routingMap); + + storeModel.SetCaches(mockPartitionKeyRangeCache.Object, mockCollectionCache.Object); + + DocumentServiceRequest request = CreatePartitionedDocumentRequest(); + + MockThinClientStoreClient mockThinClientStoreClient = new MockThinClientStoreClient( + (DocumentServiceRequest req, ResourceType resourceType, Uri uri, Uri endpoint, string globalDatabaseAccountName, ClientCollectionCache clientCollectionCache, CancellationToken cancellationToken) => + { + MemoryStream stream = new MemoryStream(new byte[] { 1, 2, 3 }); + INameValueCollection headers = new StoreResponseNameValueCollection(); + return Task.FromResult(new DocumentServiceResponse(stream, headers, HttpStatusCode.OK)); + }); + + ReplaceThinClientStoreClientField(storeModel, mockThinClientStoreClient); + + // Act + await storeModel.ProcessMessageAsync(request); + + // Assert + globalPartitionEndpointManager.Verify(m => m.TryAddPartitionLevelLocationOverride(It.IsAny()), Times.Once()); } [TestMethod] public void CircuitBreaker_MarksPartitionUnavailableOnRepeatedFailures() { - Mock mockDocumentClient = new Mock(); - mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); - ConnectionPolicy connectionPolicy = new ConnectionPolicy(); + Mock mockDocumentClient = new Mock(); + mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); + ConnectionPolicy connectionPolicy = new ConnectionPolicy(); GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, connectionPolicy); Mock globalPartitionEndpointManager = new Mock(); globalPartitionEndpointManager @@ -444,7 +444,7 @@ public void CircuitBreaker_MarksPartitionUnavailableOnRepeatedFailures() ISessionContainer sessionContainer = new Mock().Object; DocumentClientEventSource eventSource = new Mock().Object; Newtonsoft.Json.JsonSerializerSettings serializerSettings = new Newtonsoft.Json.JsonSerializerSettings(); - CosmosHttpClient httpClient = new Mock().Object; + CosmosHttpClient httpClient = new Mock().Object; Cosmos.UserAgentContainer userAgentContainer = new Microsoft.Azure.Cosmos.UserAgentContainer(0, "TestFeature", "TestRegion", "TestSuffix"); ThinClientStoreModel storeModel = new ThinClientStoreModel( @@ -454,7 +454,7 @@ public void CircuitBreaker_MarksPartitionUnavailableOnRepeatedFailures() Cosmos.ConsistencyLevel.Session, eventSource, serializerSettings, - httpClient, + httpClient, userAgentContainer, isPartitionLevelFailoverEnabled: true); @@ -504,8 +504,9 @@ public MockThinClientStoreClient( Action onDispose = null) : base( httpClient: null, - eventSource: null, userAgentContainer: null, + eventSource: null, + globalPartitionEndpointManager: null, serializerSettings: null) { this.invokeAsyncFunc = invokeAsyncFunc; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceTests.cs index 07472a3a44..203561d638 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceTests.cs @@ -4,8 +4,8 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; + using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Diagnostics; - using Microsoft.Azure.Cosmos.Json; using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Cosmos.Tracing.TraceData; using Microsoft.Azure.Documents; @@ -23,6 +23,7 @@ public void TestRootTrace() using (rootTrace = Trace.GetRootTrace(name: "RootTrace")) { Assert.IsNotNull(rootTrace); + rootTrace.SetWalkingStateRecursively(); Assert.IsNotNull(rootTrace.Children); Assert.AreEqual(0, rootTrace.Children.Count); Assert.AreEqual(rootTrace.Component, TraceComponent.Unknown); @@ -49,6 +50,7 @@ public void TestAddChild() rootTrace.AddChild(twoChild); } + rootTrace.SetWalkingStateRecursively(); Assert.AreEqual(2, rootTrace.Children.Count); Assert.AreEqual(oneChild, rootTrace.Children[0]); Assert.AreEqual(twoChild, rootTrace.Children[1]); @@ -67,6 +69,7 @@ public void TestTraceChildren() { } + rootTrace.SetWalkingStateRecursively(); Assert.AreEqual(rootTrace.Children.Count, 2); Assert.AreEqual(rootTrace.Children[0].Component, TraceComponent.Query); Assert.AreEqual(rootTrace.Children[1].Component, TraceComponent.Transport); @@ -134,5 +137,53 @@ public void ValidateStoreResultSerialization() Assert.AreEqual(0, storeResultProperties.Count, $"Json is missing properties: {string.Join(';', storeResultProperties)}"); } + + [TestMethod] + public void TestAddOrUpdateDatumThreadSafety() + { + Trace trace = Trace.GetRootTrace("ThreadSafetyTest"); + + // Create multiple threads to access the dictionary concurrently + const int numThreads = 10; + const int operationsPerThread = 100; + + // Use a list to keep track of the tasks + List tasks = new List(); + + for (int i = 0; i < numThreads; i++) + { + int threadIndex = i; + tasks.Add(Task.Run(() => + { + for (int j = 0; j < operationsPerThread; j++) + { + string key = $"key_{threadIndex}_{j}"; + object value = j; + + // Perform operations that would previously cause thread safety issues + trace.AddOrUpdateDatum(key, value); + + // Also test AddDatum + try + { + trace.AddDatum($"add_{threadIndex}_{j}", value); + } + catch (ArgumentException) + { + // Ignore key already exists exceptions which may occur + // when threads try to add the same key + } + } + })); + } + + // Wait for all tasks to complete + Task.WaitAll(tasks.ToArray()); + + trace.SetWalkingStateRecursively(); + + // Verify the data dictionary has entries + Assert.IsTrue(trace.Data.Count > 0); + } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs index 0c95ccc308..5118ccd56a 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs @@ -243,8 +243,7 @@ public void Serialization() } [TestMethod] - [Timeout(5000)] - [Ignore] + [Timeout(5000)] public void TraceData() { List inputs = new List(); @@ -757,13 +756,14 @@ private static IQueryPipelineStage CreatePipeline(IDocumentContainer documentCon new SqlQuerySpec(query), targetRanges: new List() { FeedRangeEpk.FullRange }, partitionKey: null, - GetQueryPlan(query), + GetQueryPlan(query), hybridSearchQueryInfo: null, maxItemCount: pageSize, - containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), - allRanges: new List() { FeedRangeEpk.FullRange }, + containerQueryProperties: new Cosmos.Query.Core.QueryClient.ContainerQueryProperties(), + allRanges: new List() { FeedRangeEpk.FullRange }, isContinuationExpected: true, maxConcurrency: 10, + fullTextScoreScope: FullTextScoreScope.Global, requestContinuationToken: state); tryCreatePipeline.ThrowIfFailed(); @@ -906,6 +906,8 @@ public TraceForBaselineTesting( public IReadOnlyDictionary Data => this.data; + public bool IsBeingWalked => true; // needs to return true to allow materialization + public IReadOnlyList<(string, Uri)> RegionsContacted => new List<(string, Uri)>(); public void AddDatum(string key, TraceDatum traceDatum) @@ -952,6 +954,11 @@ public void UpdateRegionContacted(TraceDatum _) public void AddOrUpdateDatum(string key, object value) { this.data[key] = value; + } + + bool ITrace.TryGetDatum(string key, out object datum) + { + return this.data.TryGetValue(key, out datum); } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockDocumentClient.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockDocumentClient.cs index 0c97035e4b..ce0a02fa21 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockDocumentClient.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockDocumentClient.cs @@ -28,10 +28,11 @@ internal class MockDocumentClient : DocumentClient, IAuthorizationTokenProvider, Mock partitionKeyRangeCache; private readonly Cosmos.ConsistencyLevel accountConsistencyLevel; - public MockDocumentClient(ConnectionPolicy connectionPolicy = null) + public MockDocumentClient(ConnectionPolicy connectionPolicy = null, bool thinClient = false) : base(new Uri("http://localhost"), MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey, connectionPolicy) { - this.Init(); + this.Init(); + this.isThinClientEnabled = thinClient; } public MockDocumentClient(Cosmos.ConsistencyLevel accountConsistencyLevel, ConnectionPolicy connectionPolicy = null) @@ -230,7 +231,7 @@ private void Init() using GlobalEndpointManager endpointManager = new(mockDocumentClient.Object, new ConnectionPolicy()); - this.partitionKeyRangeCache = new Mock(null, null, null, endpointManager, false); + this.partitionKeyRangeCache = new Mock(null, null, null, endpointManager, false, false); this.partitionKeyRangeCache.Setup( m => m.TryLookupAsync( It.IsAny(), @@ -274,7 +275,6 @@ private void Init() this.GlobalEndpointManager, default); this.sessionContainer = sessionContainer; - } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/TestUtils.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/TestUtils.cs index ffdd8b98e7..47ea54d214 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/TestUtils.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/TestUtils.cs @@ -63,7 +63,7 @@ public static void SetupCachesInGatewayStoreModel( // Prepare mocked caches. Mock clientCollectionCache = new Mock(new SessionContainer("testhost"), storeModel, null, null, null, false); - Mock partitionKeyRangeCache = new Mock(null, storeModel, clientCollectionCache.Object, endpointManager, false); + Mock partitionKeyRangeCache = new Mock(null, storeModel, clientCollectionCache.Object, endpointManager, false, false); ContainerProperties containerProperties = ContainerProperties.CreateWithResourceId("test"); containerProperties.PartitionKey = partitionKeyDefinition; diff --git a/UpdateContracts.ps1 b/UpdateContracts.ps1 index cece416f54..ff1127b358 100644 --- a/UpdateContracts.ps1 +++ b/UpdateContracts.ps1 @@ -3,13 +3,21 @@ } #Run the Cosmos DB SDK GA contract tests -dotnet test '.\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Microsoft.Azure.Cosmos.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release +dotnet test '.\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Microsoft.Azure.Cosmos.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release -f net6.0 -$updatedContractFile = ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\bin\Release\net6.0\Contracts\DotNetSDKAPIChanges.json" +$updatedContractFile = ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\bin\Release\net6.0\Contracts\DotNetSDKAPIChanges.net6.json" if(!(Test-Path -Path $updatedContractFile)){ Write-Error ("The contract file did not get updated with the build. Please fix the test to output the contract file: " + $updatedContractFile) }else{ - Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Contracts\DotNetSDKAPI.json" + Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Contracts\DotNetSDKAPI.net6.json" + Write-Output ("Updated contract " + $updatedContractFile) +} + +$updatedContractFile = ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\bin\Release\net6.0\Contracts\DotNetSDKTelemetryAPIChanges.net6.json" +if(!(Test-Path -Path $updatedContractFile)){ + Write-Error ("The contract file did not get updated with the build. Please fix the test to output the contract file: " + $updatedContractFile) +}else{ + Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Contracts\DotNetSDKTelemetryAPI.net6.json" Write-Output ("Updated contract " + $updatedContractFile) } @@ -30,7 +38,7 @@ if(!(Test-Path -Path $updatedContractFolder)){ } #Run the Cosmos DB SDK Emulator contract tests -dotnet test '.\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.EmulatorTests\Microsoft.Azure.Cosmos.EmulatorTests.csproj' --filter "TestCategory=UpdateContract" --configuration Release +dotnet test '.\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.EmulatorTests\Microsoft.Azure.Cosmos.EmulatorTests.csproj' --filter "TestCategory=UpdateContract" --configuration Release -f net6.0 $updatedContractFolder = ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.EmulatorTests\bin\Release\net6.0\BaselineTest\TestOutput\*" if(!(Test-Path -Path $updatedContractFolder)){ @@ -41,34 +49,70 @@ if(!(Test-Path -Path $updatedContractFolder)){ } #Run the Cosmos DB SDK Preview contract tests -dotnet test '.\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Microsoft.Azure.Cosmos.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release -p:IsPreview=true +dotnet test '.\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Microsoft.Azure.Cosmos.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release -p:IsPreview=true -f net6.0 -$updatedContractFile = ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\bin\Release\net6.0\Contracts\DotNetPreviewSDKAPIChanges.json" +$updatedContractFile = ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\bin\Release\net6.0\Contracts\DotNetPreviewSDKAPIChanges.net6.json" if(!(Test-Path -Path $updatedContractFile)){ Write-Error ("The contract file did not get updated with the preview build. Please fix the test to output the contract file: " + $updatedContractFile) }else{ - Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Contracts\DotNetPreviewSDKAPI.json" + Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos\tests\Microsoft.Azure.Cosmos.Tests\Contracts\DotNetPreviewSDKAPI.net6.json" Write-Output ("Updated contract " + $updatedContractFile) } #Run the Encryption SDK contract tests -dotnet test '.\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\Microsoft.Azure.Cosmos.Encryption.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release +dotnet test '.\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\Microsoft.Azure.Cosmos.Encryption.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release -f net6.0 -$updatedContractFile = ".\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\bin\Release\net6.0\Contracts\DotNetSDKEncryptionAPIChanges.json" +$updatedContractFile = ".\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\bin\Release\net6.0\Contracts\DotNetSDKEncryptionAPIChanges.net6.json" if(!(Test-Path -Path $updatedContractFile)){ Write-Error ("The contract file did not get updated with the build. Please fix the test to output the contract file: " + $updatedContractFile) }else{ - Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\Contracts\DotNetSDKEncryptionAPI.json" + Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\Contracts\DotNetSDKEncryptionAPI.net6.json" Write-Output ("Updated contract " + $updatedContractFile) } +try { + dotnet test '.\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\Microsoft.Azure.Cosmos.Encryption.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release -f net8.0 + $updatedContractFileNet8 = ".\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\bin\Release\net8.0\Contracts\DotNetSDKEncryptionAPIChanges.net8.json" + if (Test-Path -Path $updatedContractFileNet8) { + Copy-Item -Path $updatedContractFileNet8 -Destination ".\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\Contracts\DotNetSDKEncryptionAPI.net8.json" + Write-Output ("Updated .NET 8 contract " + $updatedContractFileNet8) + + # Also copy to Debug output directories so tests work in both configurations + $debugNet8Path = ".\Microsoft.Azure.Cosmos.Encryption\tests\Microsoft.Azure.Cosmos.Encryption.Tests\bin\Debug\net8.0\Contracts\DotNetSDKEncryptionAPI.net8.json" + if (Test-Path -Path (Split-Path $debugNet8Path)) { + Copy-Item -Path $updatedContractFileNet8 -Destination $debugNet8Path -Force + } + } +} catch { + Write-Warning "Unable to run net8.0 Encryption tests to produce .NET 8 baseline. Skipping." +} + #Run the Encryption.Custom SDK contract tests -dotnet test '.\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release +dotnet test '.\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release -f net6.0 -$updatedContractFile = ".\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\bin\Release\net6.0\Contracts\DotNetSDKEncryptionCustomAPIChanges.json" +$updatedContractFile = ".\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\bin\Release\net6.0\Contracts\DotNetSDKEncryptionCustomAPIChanges.net6.json" if(!(Test-Path -Path $updatedContractFile)){ Write-Error ("The contract file did not get updated with the build. Please fix the test to output the contract file: " + $updatedContractFile) }else{ - Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\Contracts\DotNetSDKEncryptionCustomAPI.json" + Copy-Item -Path $updatedContractFile -Destination ".\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\Contracts\DotNetSDKEncryptionCustomAPI.net6.json" Write-Output ("Updated contract " + $updatedContractFile) } + +# Try to generate and copy .NET 8 specific baselines if net8 target exists +try { + dotnet test '.\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests.csproj' --filter "TestCategory=UpdateContract" --configuration Release -f net8.0 + # .NET 8 test now generates a framework-specific breaking changes file + $updatedContractFileNet8 = ".\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\bin\Release\net8.0\Contracts\DotNetSDKEncryptionCustomAPIChanges.net8.json" + if (Test-Path -Path $updatedContractFileNet8) { + Copy-Item -Path $updatedContractFileNet8 -Destination ".\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\Contracts\DotNetSDKEncryptionCustomAPI.net8.json" + Write-Output ("Updated .NET 8 contract " + $updatedContractFileNet8) + + # Also copy to Debug output directories so tests work in both configurations + $debugNet8Path = ".\Microsoft.Azure.Cosmos.Encryption.Custom\tests\Microsoft.Azure.Cosmos.Encryption.Custom.Tests\bin\Debug\net8.0\Contracts\DotNetSDKEncryptionCustomAPI.net8.json" + if (Test-Path -Path (Split-Path $debugNet8Path)) { + Copy-Item -Path $updatedContractFileNet8 -Destination $debugNet8Path -Force + } + } +} catch { + Write-Warning "Unable to run net8.0 Encryption.Custom tests to produce .NET 8 baseline. Skipping." +} diff --git a/azure-pipelines-msdata-aot.yml b/azure-pipelines-msdata-aot.yml new file mode 100644 index 0000000000..f53a72b4c6 --- /dev/null +++ b/azure-pipelines-msdata-aot.yml @@ -0,0 +1,29 @@ +# A pipeline with no CI trigger +trigger: none + +pr: none + +variables: + DebugArguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional" --verbosity normal ' + ReleaseArguments: ' --filter "TestCategory!=Quarantine" --verbosity normal ' + VmImage: windows-latest # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops + +jobs: +- template: templates/static-tools.yml + parameters: + BuildConfiguration: Release + VmImage: $(VmImage) + +- template: templates/build-internal.yml + parameters: + BuildConfiguration: Release + Arguments: $(ReleaseArguments) + VmImage: $(VmImage) + +- template: templates/nuget-pack.yml + parameters: + BuildConfiguration: Release + VmImage: $(VmImage) + ReleasePackage: true + OutputPath: '$(Build.ArtifactStagingDirectory)/bin/AnyCPU/Release/Microsoft.Azure.Cosmos' + BlobVersion: 'aot/$(BlobVersion)' diff --git a/azure-pipelines-msdata-direct.yml b/azure-pipelines-msdata-direct.yml index a031d916df..741543fae4 100644 --- a/azure-pipelines-msdata-direct.yml +++ b/azure-pipelines-msdata-direct.yml @@ -65,5 +65,6 @@ jobs: Arguments: $(ReleaseArguments) VmImage: $(VmImage) ThinClientConnectionString: $(COSMOSDB_THINCLIENT) + ThinClientStrongConsistencyString: $(COSMOSDB_THINCLIENTSTRONG) IncludePerformance: true IncludeCoverage: true \ No newline at end of file diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 34abefe31d..76ba2919a3 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -49,6 +49,7 @@ stages: version: '8.x' - task: DotNetCoreCLI@2 + timeoutInMinutes: 120 displayName: Integration Test With Client Telemetry Service condition: succeeded() inputs: diff --git a/azure-pipelines-rolling.yml b/azure-pipelines-rolling.yml index 7f9ed358ef..dbd18de606 100644 --- a/azure-pipelines-rolling.yml +++ b/azure-pipelines-rolling.yml @@ -18,7 +18,7 @@ schedules: always: true # whether to always run the pipeline or only if there have been source code changes since the last run. The default is false variables: - ReleaseArguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional" --verbosity normal ' + ReleaseArguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=Ignore" --verbosity normal --logger "console;verbosity=detailed" --blame-hang --blame-crash' VmImage: windows-latest # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops jobs: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 14b5f392fa..0b5d29bed6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,8 +15,8 @@ pr: - 'Microsoft.Azure.Cosmos/contracts/**/*' variables: - DebugArguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional" --verbosity normal ' - ReleaseArguments: ' --filter "TestCategory!=Quarantine" --verbosity normal ' + DebugArguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=Ignore" --verbosity normal ' + ReleaseArguments: ' --filter "TestCategory!=Quarantine & TestCategory!=Ignore" --verbosity normal ' VmImage: windows-latest # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops @@ -79,6 +79,7 @@ jobs: Arguments: $(ReleaseArguments) VmImage: $(VmImage) ThinClientConnectionString: $(COSMOSDB_THINCLIENT) + ThinClientStrongConsistencyString: $(COSMOSDB_THINCLIENTSTRONG) MultiRegionConnectionString: $(COSMOSDB_MULTI_REGION) IncludePerformance: true IncludeCoverage: true \ No newline at end of file diff --git a/changelog.md b/changelog.md index ea0f6f81ba..cbd6fbea09 100644 --- a/changelog.md +++ b/changelog.md @@ -15,6 +15,120 @@ Preview features are treated as a separate branch and will not be included in th The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +### [3.58.0-preview.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.58.0-preview.0) - 2026-1-15 + +### [3.57.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.57.0) - 2026-1-15 + +#### Added +- [5511](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5511) Tracing: Adds tracing improvements for pkrange refresh calls +- [5515](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5515) [FullTextPolicy]: Adds tests for full text policy multi-language support. +- [5529](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5529) [Thin Client Integration]: Adds support for store procedure in thinclient mode. +- [5535](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5535) [Thin Client Integration]: Adds thinclient header for refresh account data requests. + +#### Fixed + +- [5512](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5512) ChangeFeed: Fixes crts field being nullable +- [5517](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5517) SystemTextSerializer: Fixes serialization to preserve polymorphic serialization when base type is marked [JsonPolymorphic] +- [5498](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5498) Query: Fixes hybrid search query plan optimization to be enabled by default +- [5543](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5543) Query: Fixes GetItemQueryIterator to honor the supplied (optional) FeedRange +- [5541](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5541) Upsert/Batch: Fixes bug where RequestOptions are not honored for Upsert requests in Bulk Mode +- [5544](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5544) Query : Fixes LINQ API to support builtin functions - ARRAY_CONTAINS_ALL and ARRAY_CONTAINS_ANY + + +### [3.57.0-preview.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.57.0-preview.1) - 2025-12-16 +#### Fixed +- [5528](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5528) Semantic Reranking: Refactors RerankResult.Document to return string type + +### [3.57.0-preview.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.57.0-preview.0) - 2025-11-25 +#### Added +- [5445](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5445) Semantic Rerank: Adds Semantic Rerank API + +### [3.56.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.56.0) - 2025-11-25 + +#### Fixed + +- [5550](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5550) Owner not found: Fixes substatus code 1003 for item operations when container doesn't exist (Direct mode) + +### [3.56.0-preview.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.56.0-preview.0) - 2025-11-14 +#### Fixed +- [5260](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5260) HPK: Fixes Lengthaware normalized EPK comparators as default (only in preview) + +### [3.55.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.55.0) - 2025-11-14 +#### Added +- [5462](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5462) [VectorIndexPath]: Adds GA IndexingSearchListSize and VectorIndexShardKey +- [5470](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5470) Change feed: Adds id and pk to ChangeFeedMetadata for delete operations (Default policy excludes Previous for deletes) +- [5474](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5474) Binary Encoding: Adds support to DateTimeOffset type + + +#### Fixed +- [5469](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5469) BarrierRequests: Adds 410/LeaseNotFound(1022) fail-fast to cross-region retries by retrying on primary (checks last replica response) +- [5475](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5475) Query: Fixes query advisor prefix url links to use aka.ms +- [5476](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5476) HttpTimeoutPolicy: Fixes QueryPlan retry gaps (Http POST but its Read) +- [5497](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5497) HttpTimeoutPolicy: Fixes PPAF and ThinProxy timeout polices to (6s, 6s, 10s) for both PointReads and NonPointReads + +### [3.55.1-preview.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.55.1-preview.0) - 2025-10-27 + +### [3.54.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.54.1) - 2025-10-27 +#### Fixed + +- [5455](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5455) Diagnostics: Fixes possible Thread contention in Traces +- [5446](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5446) Diagnostics: Removes Unnecessary space added by CosmosException.ToString() + +### [3.55.0-preview.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.55.0-preview.0) - 2025-10-2 + +### [3.54.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.54.0) - 2025-10-2 + +> Note: FullTextScore query function now expects string value parameters (e.g., FullTextScore(c.text, 'swim','run')); preview array syntax is no longer supported. +> Updates Direct Package version update to 3.41.3 which includes the following: + * Fixes barrier calls to ensure calls don’t reuse stale strong‑write context after AccountRefresh by persisting the initial write endpoint and failing fast on cross‑region retries. + +#### Added + +- [5368](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5368) VectorDataType: Adds Support for Float16 Data Type +- [5411](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5411) Hedging: Adds GA for adding hedging via RequestOptions +- [5412](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5412) Hedging: Adds back diagnostics filed Response Region to hedging request diagnostics +- [5386](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5386) Build: Removes System.Net.Http and System.Text.RegularExpressions package references + +#### Fixed + +- [5409](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5409) Query: Fixes to not use passthrough context for collections with HPK +- [5422](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5422) Diagnostics: Fixes race condition that can cause InvalidOperationException in CosmosOperationCancelledException.ToString() +- [5427](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5427) PPAF: Fixes issue where setting RequestTimeout to 0 second will cause PPAF dynamic enablement to break + +## [3.54.0-preview.2](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.54.0-preview.2) - 2025-10-7 + +### [3.53.2](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.53.2) - 2025-10-7 + +#### Fixed + +- [5427](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5427) PPAF: Fixes issue where setting RequestTimeout to 0 second will cause PPAF dynamic enablement to break + + +### [3.54.0-preview.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.54.0-preview.1) - 2025-8-27 + +### [3.53.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.53.1) - 2025-8-27 + +#### Added +- [5364](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5364) TokenCredentialCache: Adds a fallback mechanism to AAD scope override. +- [5361](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5361) Trace: Fixes thread safety issue in Trace class causing high CPU usage and InvalidOperationException + +### [3.54.0-preview.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.54.0-preview.0) - 2025-8-13 + +### [3.53.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.53.0) - 2025-8-13 + +#### Added + +- [5253](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5253) License: Adds new license expression +- [5252](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5252) TokenCredentialCache: Adds an options to override AAD audience scope +- [5308](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5308) Query: Adds Weighted RRF capability to LINQ +- [5213](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5213) Query: Adds GetIndexMetrics LINQ extension method + +#### Fixed + +- [5273](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5273) Query: Fixes non streaming order by queries to not be tagged as passthrough queries +- [5291](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5291) GatewayStoreClient: Fixes stream consumption bug in GatewayStoreClient.CreateDocumentClientExceptionAsync +- [5317](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5317) Query: Fixes HybridSearchQueryTests to account for backend changes in phrase search + ### [3.53.0-preview.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.53.0-preview.1) - 2025-7-10 ### [3.52.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.52.1) - 2025-7-10 @@ -626,6 +740,9 @@ If you have a scenario where tokens generated from the newer SDKs are used by an - [#3787](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3787) Connectivity: Fixes ConnectionBroken and adds support for Burst Capacity ### [3.32.2](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.32.2) - 2023-03-10 + +> Note: If users on this version face an `IndexOutOfRangeException` and observe connections getting unexpectedly closed, they should upgrade to version `3.32.3` or higher, where this issue has been resolved. + ### [3.32.2-preview](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.32.2-preview) - 2023-03-10 #### Fixed @@ -633,12 +750,18 @@ If you have a scenario where tokens generated from the newer SDKs are used by an - [#3749](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3749) Query: Fixes regression from LINQ custom serializer fix. Introduced in 3.32.0 PR [3749](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3749) ### [3.32.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.32.1) - 2023-03-01 + +> Note: If users on this version face an `IndexOutOfRangeException` and observe connections getting unexpectedly closed, they should upgrade to version `3.32.3` or higher, where this issue has been resolved. + ### [3.32.1-preview](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.32.1-preview) - 2023-03-01 #### Fixed - [#3732](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3732) ReadMany: Fixes BadRequest when using Ids with single quotes ### [3.32.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.32.0) - 2023-02-03 + +> Note: If users on this version face an `IndexOutOfRangeException` and observe connections getting unexpectedly closed, they should upgrade to version `3.32.3` or higher, where this issue has been resolved. + #### Fixed - [#3466](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3466) ClientRetryPolicy: Fixes behavior to Meta-data write operations in multimaster accounts - [#3498](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3498) PartitionKey: Fixes NullRef in toString handling for None for PartitionKey.ToString() @@ -1716,6 +1839,7 @@ Below is a list of any know issues affecting the [recommended minimum version](# | Issue | Impact | Mitigation | Tracking link | | --- | --- | --- | --- | +| Calling `GetItemQueryIterator` with FeedRange | Scenarios that use FeedRange with min excluded or max included while calling `GetItemQueryIterator`. | `FeedIterator` created by calling `GetItemQueryIterator` while supplying FeedRange ignores the max/min inclusion properties. The returned iterator _always_ includes min and excludes max implicitly. This will be tracked and fixed as a separate issue. As a workaround, only use FeedRange values with min included and max excluded. | TBD | | Optimistic Direct Execution in case of Partition Split/Merge. | Scenarios that enable optimistic direct execution. | Optimistic Direct Execution may result in incorrect results when partition split/merge occurs in the backend while query execution is in progress. General recommendation is disable optimistic direct execution while executing queries. | [#4971](https://github.com/Azure/azure-cosmos-dotnet-v3/issues/4971) | | Optimistic Direct execution continuation token. | Scenarios that enable optimistic direct execution. | Optimistic Direct Execution may produce a continuation token that is rejected by SDK after partition split. General recommendation is disable optimistic direct execution while executing queries. | [#4972](https://github.com/Azure/azure-cosmos-dotnet-v3/issues/4972) | | `FeedIterator` enters an infinite loop after a physical partition split occurs in a container using hierarchical partition keys. | Queries using prefix partition keys. | Rather than having the PK included in the query request options, filtering on top level hierarchical Pks should be done through where clauses. **NOTE:** This issue has been fixed in version 3.39.0 | [#4326](https://github.com/Azure/azure-cosmos-dotnet-v3/issues/4326) | diff --git a/docs/query/local_statistics_for_hybrid_search.md b/docs/query/local_statistics_for_hybrid_search.md new file mode 100644 index 0000000000..f9527aad32 --- /dev/null +++ b/docs/query/local_statistics_for_hybrid_search.md @@ -0,0 +1,117 @@ +# Enabling users to choose global vs local/focused statistics for FullTextScore + +## Why? + +Cosmos DB’s implementation of FullTextScore computes BM25 statistics (term frequency, inverse document frequency, and document length) across all documents in the container, including all physical and logical partitions. + +While this provides a valid and comprehensive representation of statistics for the entire dataset, it introduces challenges for several common use cases. + +In multi-tenant scenarios, it is often necessary to isolate queries to data belonging to a specific tenant, typically defined by the partition key or a component of a hierarchical partition key. This enables scoring to reflect statistics that are accurate for that tenant’s dataset, rather than for the entire container. For customers such as Veeam and Sitecore, which operate large multi-tenant containers, this is not just an optimization but a requirement. Their tenants often operate in very different domains, which can significantly change the distribution and importance of keywords and phrases. Using global statistics in these cases leads to distorted relevance rankings. + +In other scenarios involving hundreds or thousands of physical partitions, computing statistics across the entire container can become both time-consuming and expensive. Customers may prefer to use statistics derived from only a subset of partitions to improve performance and reduce RU consumption. Indeed, there is precedence for this as Azure AI Search defaults to this “local” method. + +## What? + +We propose extending the flexibility of BM25 scoring in Cosmos DB so that developers can choose between a *global FullTextScore* (existing behavior) or *Scoped FullTextScore* (statistics computed restricted to the partition key(s) used in the query). The key aspects: + +For _*global*_ BM25, FullTextScore retains its existing behavior and computes BM25 statistics, such as IDF and average document length, across all documents in the container regardless of any partition key filters in the query. In _*scoped*_ BM25, when a query includes a partition key filter or explicitly requests scoped scoring, the engine computes these statistics only over the subset of documents within the specified partition key values. Query results are still returned only from the filtered partitions, and the resulting scores and ranking reflect relevance within that partition-specific slice of data. + +## How? + +The user issues query like: + +``` +SELECT TOP 10 * FROM c +WHERE c.tenantId = @tenantId +ORDER BY RANK FullTextScore(c.text, "keywords") +``` + + +And sets a new [QueryRequestOption](../Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs) called `FullTextScoreScope` which can be set to one of two values: `local` or `global`. The request option is inspected, and the query uses scoped/full stats accordingly. + +### Pros: + +- No change required to SQL syntax or function call, transparent to existing queries (if default global). +- Good separation of concerns between ranking logic and query syntax; developer chooses via configuration rather than code change. + +### Cons: + +- Requires SDK/API layer change and tooling to support the new request parameter, less discoverable in SQL alone. +- Harder for query authoring (especially interactive) to know which mode is used, less transparent in the query text. +- Might limit granularity: if query has multiple FullTextScore calls maybe you cannot mix scope modes (depending on implementation), less flexible than in-call parameter. + + +## Implementation Plan + +Create a new member in [QueryRequestOptions.cs](../Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs) called `FullTextScoreScope` which should be an enum with two possible values: `Local` or `Global`. The default value in `QueryRequestOptions` should be `Global` + +The `FullTextScoreScope` from the `QueryRequestOptions` needs to be plumbed to `HybridSearchCrossPartitionQueryPipelineStage.MonadicCreate` defined in [HybridSearchCrossPartitionQueryPipelineStage.cs](../Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CrossPartition/HybridSearch/HybridSearchCrossPartitionQueryPipelineStage.cs). In the `HybridSearchCrossPartitionQueryPipelineStage` constructor when creating the `tryCatchGlobalStatisticsPipeline`, if the `FullTextScoreScope` is set to `Global` then `allRanges` is passed in to the `targetRanges` parameter else the `targetRanges` are passed in. + +## Test Plan: FullTextScoreScope Validation in NonStreamingOrderByQueryTests.cs + +### Status: ✅ COMPLETE + +### Objective +Validate that the `FullTextScoreScope` feature correctly controls whether BM25 statistics are computed globally (across all partitions) or locally (scoped to filtered partitions) in hybrid search queries. + +### Implementation Summary + +#### Changes Made to `NonStreamingOrderByQueryTests.cs`: + +1. **`HybridSearchTest` class**: Added `FullTextScoreScope` and `TargetRangeCount` properties + +2. **`MockDocumentContainer` class**: Added `statisticsQueryRanges` list to track which ranges receive statistics queries + +3. **Factory methods**: Updated `MakeHybridSearchTest` and `MakeHybridSearchSkipOrderByRewriteTest` to accept the new parameters + +4. **`RunHybridSearchTest`**: + - Now calculates expected indices for subset queries (interleaved document distribution) + - Validates statistics query ranges match expected scope (Global = all ranges, Local = target ranges only) + +5. **`CreateAndRunHybridSearchQueryPipelineStage`**: Updated to accept separate `targetRanges`, `allRanges`, and `fullTextScoreScope` parameters + +#### Test Cases Added: + +**`HybridSearchTests()`** - 7 new Local scope test cases: +- Local scope with 2 target ranges +- Local scope with 3 target ranges and skip/take +- Local scope with single target range +- Local scope with larger page size than document count +- Local scope with skip and take +- Local scope with small page size (multiple pages) +- Local scope with 4 target ranges (majority of 6) + +**`HybridSearchSkipOrderByRewriteTests()`** - 5 new Local scope test cases: +- Local scope with 2 target ranges +- Local scope with single target range +- Local scope with small page size +- Local scope with different skip/take values +- Local scope with 4 target ranges + +**`HybridSearchWeightedRRFTests()`** - 5 new Local scope test cases: +- Local scope with equal weights +- Local scope with different weights (0.5, 1.5) +- Local scope with single target range and weights +- Local scope with small page size and weights +- Local scope with 4 target ranges and weights + +**`HybridSearchDefaultScopeIsGlobalTests()`**: +- Verifies default `QueryRequestOptions.FullTextScoreScope` is `Global` + +### Test Results +All 5 hybrid search test methods pass: +- `HybridSearchDefaultScopeIsGlobalTests` ✅ +- `HybridSearchTests` ✅ +- `HybridSearchSkipOrderByRewriteTests` ✅ +- `HybridSearchWeightedRRFTests` ✅ +- `HybridSearchSkipOrderByRewriteWeightedRRFTests` ✅ + +### Key Validation Points + +| Scenario | FullTextScoreScope | TargetRangeCount | Statistics Query Ranges | +|----------|-------------------|------------------|------------------------| +| All ranges (default) | Global | null (all 6) | All 6 ranges | +| Subset with Global | Global | 2 | All 6 ranges | +| Subset with Local | Local | 2 | Only 2 target ranges | +| Single range Local | Local | 1 | Only 1 target range | +| Majority ranges Local | Local | 4 | Only 4 target ranges | diff --git a/docs/sync_up_msdata_direct.md b/docs/sync_up_msdata_direct.md index 9c416aa65b..ddefdf0f34 100644 --- a/docs/sync_up_msdata_direct.md +++ b/docs/sync_up_msdata_direct.md @@ -8,6 +8,7 @@ * [Validating the sync-up.](#validating-the-sync-up) * [Submit Pull Request to msdata direct.](#submit-pull-request-to-msdata-direct) * [Sample Pull Requests to Sync-up msdata direct.](#sample-pull-requests-to-sync-up-msdata-direct) +* [Common Issues and Resolutions.](#common-issues-and-resolutions) ## Background @@ -17,6 +18,8 @@ As a developer on the Cosmos SDK team, we often engage in a task, that requires Before covering the sync-up process in detail, please follow the below steps to make sure all the required pre-requisites are met. +> **Note for AI Agents**: The sync process requires the local path to the msdata `CosmosDB` repository. If the user does not provide this path in their instructions, **ask for it before proceeding**. The path is needed to update `$baseDir` in `msdata_sync.ps1` and to manually copy any missing files. Do not assume a default path. + ### Clone the Azure Cosmos DB .NET SDK Version 3 Repo - Clone the azure `cosmos-db dotnet sdk` repo in the local environment, using the below git command: @@ -25,12 +28,12 @@ Before covering the sync-up process in detail, please follow the below steps to - Navigate to the directory `azure-cosmos-dotnet-v3` and check out the following branch, `msdata/direct` using the below git commands: - git pull && git checkout msdata/direct -### Clone the `CosmosDB` Repo hosted in msdata +### CosmosDB Repo hosted in msdata -- Clone the CosmosDB repository in the local environment, using the CosmosDB onboarding guide. Please note, building the entire repository is not required for the sync-up process. +- Ensure you have a local clone of the CosmosDB repository (see the CosmosDB onboarding guide for cloning instructions). Building the entire repository is not required for the sync-up process. -- Navigate to the cloned `CosmosDB` directory and check out the following branch, `master` using the below git commands: - - git pull && git checkout master +- Navigate to the cloned `CosmosDB` directory and make sure it is on the latest `master`: + - git checkout master && git pull ## Steps Required to Update msdata direct Repo @@ -47,6 +50,8 @@ The next step is to port the latest `master` branch code into the newly created - Make sure the `master` branch is up-to-date. - Stay on the newly created feature branch `users//update_msdata_direct_` and run `git merge master`. - There are likely to be conflicts during the merge. If that happens, we will need to resolve the conflicts gracefully by accepting the incoming `master` branch changes. + - **Exception**: Files unique to `msdata/direct` (such as `AssemblyKeys.cs` in `Microsoft.Azure.Cosmos/src/direct/`) must be kept from `msdata/direct`, not overwritten by master. If accidentally lost, restore with: `git checkout origin/msdata/direct -- `. + - For delete/modify conflicts (files deleted on master but modified on `msdata/direct`), keep the `msdata/direct` version. ### Pick the Required Microsoft Azure Cosmos.Direct files into `msdata/direct` repo. @@ -73,7 +78,8 @@ This is the last part for the sync-up process. Please follow the below steps to Copying Files: BarrierRequestHelper.cs ``` -- Note: There may be instances where some of the files could be missing in the v3 `msdata/direct` repo and the copy may fail with the following error: `Write-Error: SystemSynchronizationScope.cs False`. If that happens, please copy the file manually from the `msdata/CosmosDB` repo and continue running the script all over again. +- Note: There may be instances where some of the files could be missing in the v3 `msdata/direct` repo and the copy may fail with the following error: `Write-Error: SystemSynchronizationScope.cs False`. If that happens, please copy the file manually from the `msdata/CosmosDB` repo and continue running the script all over again. +- Note: If new source directories were added in the msdata repo (e.g., `Rntbd\rntbdtokens\`, `Cosmos\Core\Core\Utilities\`), the script will fail to find those files. Add the new paths to `$srcDirs` in `msdata_sync.ps1` before re-running. ## Validating the sync-up @@ -81,7 +87,8 @@ One of the most important part in the whole `msdata/direct` sync up process is t - Open command prompt/ windows terminal and navigate to the directory where the cosmos v3 code is located, for instance `C:\stash\azure-cosmos-dotnet-v3`. - Make sure to stay on the newly created feature branch. -- Stay on the same directory mentioned above, and run the following command for a clean build: `dotnet build`. Make sure, the build passes successfully. +- Stay on the same directory mentioned above, and run the following command for a clean build: `dotnet build Microsoft.Azure.Cosmos\src\Microsoft.Azure.Cosmos.csproj`. Make sure, the build passes successfully. +- **Critical**: After merging `master`, verify that `Microsoft.Azure.Cosmos.csproj` still has `compile` on the `Microsoft.Azure.Cosmos.Direct` PackageReference. The merge from `master` typically overwrites this, causing thousands of CS0436 type-conflict errors between local source files and the NuGet package. ## Submit Pull Request to msdata direct @@ -90,4 +97,15 @@ Once the feature branch builds successfully, it's time to submit the PR to `msda ## Sample Pull Requests to Sync-up msdata direct - [[Internal] Msdata/Direct: Refactors msdata branch with latest v3 and direct release](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3726) -- [[Internal] Msdata/Direct: Refactors msdata/direct branch with latest v3 master and Cosmos.Direct v3.30.4](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3776) \ No newline at end of file +- [[Internal] Msdata/Direct: Refactors msdata/direct branch with latest v3 master and Cosmos.Direct v3.30.4](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3776) + +## Common Issues and Resolutions + +| Issue | Typical Fix | +|-------|------------| +| `GatewayStoreModel` fields inaccessible from `ThinClientStoreModel` | Change `private` fields/methods to `protected internal` or `internal` | +| Constructor signature mismatches (e.g., `ThinClientStoreClient`) | Update call sites to match the current constructor parameters | +| Read-only property assignments in converters | Use the backing setter (e.g., `ConflictResolutionTimestampInSeconds` instead of `ConflictResolutionTimestamp`) | +| Missing types (e.g., `AzureRbac`, `IServiceConfigurationReaderVnext`) | Copy from the msdata CosmosDB repo manually | +| XML comment errors (CS1570, CS1574, unclosed tags, unescaped `<`, `>`, `&`) | Escape special chars or fix malformed tags in the synced files | +| StyleCop / formatting errors (SA****) | Add error codes to `` in the `.csproj` — these are cosmetic and safe to suppress for the sync PR | \ No newline at end of file diff --git a/templates/build-test-msdata.yml b/templates/build-test-msdata.yml index a91df313c3..b77554c722 100644 --- a/templates/build-test-msdata.yml +++ b/templates/build-test-msdata.yml @@ -5,8 +5,8 @@ parameters: Arguments: '' VmImage: '' # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops OS: 'Windows' - EmulatorPipeline1Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed) & TestCategory !=UpdateContract" --verbosity normal ' - EmulatorPipeline2Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & TestCategory !=UpdateContract" --verbosity normal ' + EmulatorPipeline1Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed) & TestCategory !=UpdateContract & TestCategory!=Ignore" --verbosity normal ' + EmulatorPipeline2Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & TestCategory !=UpdateContract & TestCategory!=Ignore" --verbosity normal ' EmulatorPipeline3Arguments: ' --filter "TestCategory=MultiRegion" --verbosity normal ' EmulatorPipeline4Arguments: ' --filter "TestCategory=MultiMaster" --verbosity normal ' EmulatorPipeline1CategoryListName: ' Client Telemetry, Query, ChangeFeed, ReadFeed, Batch ' # Divided in 2 categories to run them in parallel and reduce the PR feedback time diff --git a/templates/build-test.yml b/templates/build-test.yml index 33bb4f3e0b..48f46e29d4 100644 --- a/templates/build-test.yml +++ b/templates/build-test.yml @@ -5,8 +5,8 @@ parameters: Arguments: '' VmImage: '' # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops OS: 'Windows' - EmulatorPipeline1Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed)" --verbosity normal ' - EmulatorPipeline2Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster" --verbosity normal ' + EmulatorPipeline1Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed) & TestCategory!=Ignore" --verbosity normal ' + EmulatorPipeline2Arguments: ' --filter "TestCategory!=Flaky & TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory !=ThinClient & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed & TestCategory!=LongRunning & TestCategory!=MultiRegion & TestCategory!=MultiMaster & TestCategory!=Ignore" --verbosity normal ' EmulatorPipeline3Arguments: ' --filter "TestCategory=MultiRegion" --verbosity normal ' EmulatorPipeline4Arguments: ' --filter "TestCategory=MultiMaster" --verbosity normal ' EmulatorPipeline1CategoryListName: ' Client Telemetry, Query, ChangeFeed, ReadFeed, Batch ' # Divided in 2 categories to run them in parallel and reduce the PR feedback time diff --git a/templates/build-thinclient.yml b/templates/build-thinclient.yml index e7dfe8a35a..d77f057da3 100644 --- a/templates/build-thinclient.yml +++ b/templates/build-thinclient.yml @@ -7,6 +7,7 @@ parameters: OS: 'Windows' EmulatorPipeline5Arguments: ' --filter "TestCategory=ThinClient" --verbosity normal ' EmulatorPipeline5CategoryListName: ' ThinClient ' + ThinClientStrongConsistencyString: '' ThinClientConnectionString: '' MultiRegionConnectionString : '' IncludeEncryption: true @@ -38,6 +39,8 @@ jobs: packageType: 'sdk' version: '8.x' + - template: emulator-setup.yml + - task: DotNetCoreCLI@2 displayName: Microsoft.Azure.Cosmos.EmulatorTests - ${{ parameters.EmulatorPipeline5CategoryListName }} continueOnError: true @@ -50,6 +53,7 @@ jobs: testRunTitle: Microsoft.Azure.Cosmos.EmulatorTests - ${{ parameters.EmulatorPipeline5CategoryListName }} env: COSMOSDB_THINCLIENT: ${{ parameters.ThinClientConnectionString }} + COSMOSDB_THINCLIENTSTRONG: ${{ parameters.ThinClientStrongConsistencyString }} COSMOSDB_MULTI_REGION: ${{ parameters.MultiRegionConnectionString }} AZURE_COSMOS_THIN_CLIENT_ENABLED: 'True' AZURE_COSMOS_NON_STREAMING_ORDER_BY_FLAG_DISABLED: 'true'