From 26739ba3a9a73f1622bf2e3ede2e77a1a277cbb0 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:50:09 +0000 Subject: [PATCH 1/6] feat(metadata): add Lambda Metadata utility for retrieving execution environment metadata - Add new AWS.Lambda.Powertools.Metadata NuGet package with LambdaMetadataClient for accessing Lambda Metadata Service - Implement LambdaMetadata class to expose AvailabilityZoneId property from LMDS endpoint - Add LambdaMetadataHttpClient for HTTP communication with automatic token and endpoint handling - Implement automatic caching of metadata for sandbox lifetime with thread-safe concurrent access - Add support for both synchronous (Get/Refresh) and asynchronous (GetAsync/RefreshAsync) operations - Add LambdaMetadataException for error handling with HTTP status code information - Implement LambdaMetadataSerializerContext for native AOT compatibility using source-generated JSON serialization - Add comprehensive unit tests covering normal operations, concurrency, and error scenarios - Add detailed documentation with usage examples, error handling, and environment variable reference - Update solution file to include new metadata project and tests --- docs/utilities/metadata.md | 270 +++ libraries/AWS.Lambda.Powertools.sln | 1592 +++++++++-------- .../AWS.Lambda.Powertools.Metadata.csproj | 21 + .../Exceptions/LambdaMetadataException.cs | 66 + .../Internal/ILambdaMetadataHttpClient.cs | 35 + .../Internal/LambdaMetadataHttpClient.cs | 186 ++ .../LambdaMetadataSerializerContext.cs | 30 + .../InternalsVisibleTo.cs | 20 + .../LambdaMetadata.cs | 64 + .../LambdaMetadataClient.cs | 243 +++ .../AWS.Lambda.Powertools.Metadata/README.md | 92 + ...WS.Lambda.Powertools.Metadata.Tests.csproj | 31 + .../LambdaMetadataExceptionTests.cs | 62 + .../Internal/LambdaMetadataHttpClientTests.cs | 189 ++ .../LambdaMetadataClientCollection.cs | 27 + .../LambdaMetadataClientConcurrencyTests.cs | 227 +++ .../LambdaMetadataClientTests.cs | 315 ++++ .../LambdaMetadataTests.cs | 72 + mkdocs.yml | 2 + 19 files changed, 2761 insertions(+), 783 deletions(-) create mode 100644 docs/utilities/metadata.md create mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/AWS.Lambda.Powertools.Metadata.csproj create mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/Exceptions/LambdaMetadataException.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/Internal/ILambdaMetadataHttpClient.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataHttpClient.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataSerializerContext.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/InternalsVisibleTo.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadata.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadataClient.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/README.md create mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/AWS.Lambda.Powertools.Metadata.Tests.csproj create mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Exceptions/LambdaMetadataExceptionTests.cs create mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/LambdaMetadataHttpClientTests.cs create mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientCollection.cs create mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientConcurrencyTests.cs create mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientTests.cs create mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataTests.cs diff --git a/docs/utilities/metadata.md b/docs/utilities/metadata.md new file mode 100644 index 00000000..6907906f --- /dev/null +++ b/docs/utilities/metadata.md @@ -0,0 +1,270 @@ +--- +title: Lambda Metadata +description: Utility +--- + + +The Lambda Metadata utility provides idiomatic access to the Lambda Metadata Endpoint (LMDS), eliminating boilerplate code for retrieving execution environment metadata like Availability Zone ID. + +## Key features + +* Retrieve Lambda execution environment metadata (e.g., Availability Zone ID) +* Automatic caching for the sandbox lifetime +* Thread-safe access for concurrent executions +* Async/await support +* Lazy loading on first access +* Native AOT compatible + +!!! warning "Migrating to v3" + + If you're upgrading to v3, please review the [Migration Guide v3](../migration-guide-v3.md) for important breaking changes including .NET 8 requirement and AWS SDK v4 migration. + +## Installation + +Powertools for AWS Lambda (.NET) are available as NuGet packages. You can install the packages from [NuGet Gallery](https://www.nuget.org/packages?q=AWS+Lambda+Powertools*){target="_blank"} or from Visual Studio editor by searching `AWS.Lambda.Powertools*` to see various utilities available. + +* [AWS.Lambda.Powertools.Metadata](https://www.nuget.org/packages?q=AWS.Lambda.Powertools.Metadata): + + `dotnet add package AWS.Lambda.Powertools.Metadata` + +## Getting started + +The Lambda Metadata utility provides a simple static client to retrieve metadata about the Lambda execution environment. + +### Basic usage + +=== "Synchronous" + + ```c# hl_lines="10-11" + using AWS.Lambda.Powertools.Metadata; + + public class Function + { + public string FunctionHandler(object input, ILambdaContext context) + { + // Retrieve metadata (cached after first call) + var metadata = LambdaMetadataClient.Get(); + + // Access the Availability Zone ID + var azId = metadata.AvailabilityZoneId; + + return $"Running in AZ: {azId}"; + } + } + ``` + +=== "Asynchronous" + + ```c# hl_lines="10-11" + using AWS.Lambda.Powertools.Metadata; + + public class Function + { + public async Task FunctionHandler(object input, ILambdaContext context) + { + // Retrieve metadata asynchronously (cached after first call) + var metadata = await LambdaMetadataClient.GetAsync(); + + // Access the Availability Zone ID + var azId = metadata.AvailabilityZoneId; + + return $"Running in AZ: {azId}"; + } + } + ``` + +### Eager loading + +For optimal performance, you can fetch metadata during cold start by using a static field initializer: + +```c# hl_lines="7-8" +using AWS.Lambda.Powertools.Metadata; + +public class Function +{ + // Fetch during cold start - metadata is cached for the sandbox lifetime + private static readonly LambdaMetadata Metadata = LambdaMetadataClient.Get(); + + public string FunctionHandler(object input, ILambdaContext context) + { + // Use cached metadata - no additional API call + return $"Running in AZ: {Metadata.AvailabilityZoneId}"; + } +} +``` + +This pattern ensures the metadata is fetched once during Lambda initialization and reused across all invocations. + +## Available metadata + +The `LambdaMetadata` class provides the following properties: + +| Property | Type | Description | +|-----------------------|----------|-----------------------------------------------------------------------------| +| `AvailabilityZoneId` | `string` | The Availability Zone ID where the Lambda function is running (e.g., `use1-az1`) | + +## Refreshing metadata + +In most cases, you won't need to refresh metadata since it remains constant for the Lambda sandbox lifetime. However, if needed: + +=== "Synchronous" + + ```c# hl_lines="5" + using AWS.Lambda.Powertools.Metadata; + + // Force a refresh of cached metadata + var metadata = LambdaMetadataClient.Refresh(); + ``` + +=== "Asynchronous" + + ```c# hl_lines="5" + using AWS.Lambda.Powertools.Metadata; + + // Force a refresh of cached metadata asynchronously + var metadata = await LambdaMetadataClient.RefreshAsync(); + ``` + +## Error handling + +The utility throws `LambdaMetadataException` when it cannot retrieve metadata: + +```c# hl_lines="9-13" +using AWS.Lambda.Powertools.Metadata; +using AWS.Lambda.Powertools.Metadata.Exceptions; + +public class Function +{ + public string FunctionHandler(object input, ILambdaContext context) + { + try + { + var metadata = LambdaMetadataClient.Get(); + return $"Running in AZ: {metadata.AvailabilityZoneId}"; + } + catch (LambdaMetadataException ex) + { + // Handle error - metadata endpoint unavailable + Console.WriteLine($"Failed to get metadata: {ex.Message}"); + + // Check HTTP status code if available + if (ex.StatusCode.HasValue) + { + Console.WriteLine($"HTTP Status: {ex.StatusCode}"); + } + + return "Unknown AZ"; + } + } +} +``` + +### Common error scenarios + +| Scenario | Exception Message | +|-----------------------------------|-------------------------------------------------------------| +| Missing metadata token | `Lambda metadata token not available. Ensure AWS_LAMBDA_METADATA_TOKEN is set.` | +| Missing metadata API endpoint | `Lambda metadata API endpoint not available. Ensure AWS_LAMBDA_METADATA_API is set.` | +| HTTP error from metadata endpoint | `Metadata request failed with status {code}: {message}` | +| Deserialization failure | `Failed to deserialize Lambda metadata response.` | + +## Environment variables + +The utility uses the following environment variables (automatically set by the Lambda runtime): + +| Environment Variable | Description | +|-----------------------------|--------------------------------------------------| +| `AWS_LAMBDA_METADATA_API` | The metadata API endpoint | +| `AWS_LAMBDA_METADATA_TOKEN` | Authentication token for the metadata API | + +!!! note + These environment variables are automatically configured by the Lambda runtime. You don't need to set them manually. + +## Thread safety + +The `LambdaMetadataClient` is fully thread-safe: + +* Uses double-check locking pattern for synchronous access +* Uses `SemaphoreSlim` for asynchronous access +* Cached metadata is stored in a `volatile` field for safe concurrent reads +* Both sync and async methods share the same cache + +This means you can safely call `Get()` or `GetAsync()` from multiple concurrent Lambda invocations without race conditions. + +## Cancellation support + +The async methods support cancellation tokens: + +```c# hl_lines="5-6" +using AWS.Lambda.Powertools.Metadata; + +public async Task FunctionHandler(object input, ILambdaContext context) +{ + using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(500)); + var metadata = await LambdaMetadataClient.GetAsync(cts.Token); + return metadata.AvailabilityZoneId; +} +``` + +## AOT support + +This utility is fully compatible with Native AOT compilation. It uses source-generated JSON serialization to avoid reflection-based deserialization. + +No additional configuration is required for AOT support. + +## Use cases + +### Multi-AZ routing decisions + +```c# +using AWS.Lambda.Powertools.Metadata; + +public class Function +{ + private static readonly LambdaMetadata Metadata = LambdaMetadataClient.Get(); + + public async Task FunctionHandler(OrderRequest request, ILambdaContext context) + { + // Route to AZ-local resources for lower latency + var azId = Metadata.AvailabilityZoneId; + var endpoint = GetAzLocalEndpoint(azId); + + return await ProcessOrder(request, endpoint); + } + + private string GetAzLocalEndpoint(string azId) + { + return azId switch + { + "use1-az1" => "https://service-az1.internal", + "use1-az2" => "https://service-az2.internal", + _ => "https://service.internal" + }; + } +} +``` + +### Logging and observability + +```c# +using AWS.Lambda.Powertools.Logging; +using AWS.Lambda.Powertools.Metadata; + +public class Function +{ + private static readonly LambdaMetadata Metadata = LambdaMetadataClient.Get(); + + public Function() + { + // Add AZ ID to all log entries + Logger.AppendKey("availability_zone_id", Metadata.AvailabilityZoneId); + } + + [Logging] + public string FunctionHandler(object input, ILambdaContext context) + { + Logger.LogInformation("Processing request"); + return "Success"; + } +} +``` diff --git a/libraries/AWS.Lambda.Powertools.sln b/libraries/AWS.Lambda.Powertools.sln index 7b472cce..d613a621 100644 --- a/libraries/AWS.Lambda.Powertools.sln +++ b/libraries/AWS.Lambda.Powertools.sln @@ -1,783 +1,809 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.3.32929.385 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{73C9B1E5-3893-47E8-B373-17E5F5D7E6F5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Common", "src\AWS.Lambda.Powertools.Common\AWS.Lambda.Powertools.Common.csproj", "{ACED222F-BDEB-48B6-BA6E-A28659080766}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Logging", "src\AWS.Lambda.Powertools.Logging\AWS.Lambda.Powertools.Logging.csproj", "{7A6A2996-1F41-4C94-A0A2-3AE963247243}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metrics", "src\AWS.Lambda.Powertools.Metrics\AWS.Lambda.Powertools.Metrics.csproj", "{3BA6251D-DE4E-4547-AAA9-25F4BA04C636}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Tracing", "src\AWS.Lambda.Powertools.Tracing\AWS.Lambda.Powertools.Tracing.csproj", "{1A3AC28C-3AEE-40FE-B229-9E38BB609547}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1CFF5568-8486-475F-81F6-06105C437528}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Logging.Tests", "tests\AWS.Lambda.Powertools.Logging.Tests\AWS.Lambda.Powertools.Logging.Tests.csproj", "{B68A0D0A-4785-48CB-864F-29E3A8ACA526}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metrics.Tests", "tests\AWS.Lambda.Powertools.Metrics.Tests\AWS.Lambda.Powertools.Metrics.Tests.csproj", "{A422C742-2CF9-409D-BDAE-15825AB62113}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Tracing.Tests", "tests\AWS.Lambda.Powertools.Tracing.Tests\AWS.Lambda.Powertools.Tracing.Tests.csproj", "{A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Common.Tests", "tests\AWS.Lambda.Powertools.Common.Tests\AWS.Lambda.Powertools.Common.Tests.csproj", "{4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.BatchProcessing", "src\AWS.Lambda.Powertools.BatchProcessing\AWS.Lambda.Powertools.BatchProcessing.csproj", "{F8B4100F-4014-4A1E-8130-D281453B79ED}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.BatchProcessing.Tests", "tests\AWS.Lambda.Powertools.BatchProcessing.Tests\AWS.Lambda.Powertools.BatchProcessing.Tests.csproj", "{12B940EF-A5D3-459D-BD36-A603834D1F7D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Idempotency", "src\AWS.Lambda.Powertools.Idempotency\AWS.Lambda.Powertools.Idempotency.csproj", "{B7AC87DF-9705-47D9-AC00-C230E577CA5D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Idempotency.Tests", "tests\AWS.Lambda.Powertools.Idempotency.Tests\AWS.Lambda.Powertools.Idempotency.Tests.csproj", "{3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Parameters", "src\AWS.Lambda.Powertools.Parameters\AWS.Lambda.Powertools.Parameters.csproj", "{1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Parameters.Tests", "tests\AWS.Lambda.Powertools.Parameters.Tests\AWS.Lambda.Powertools.Parameters.Tests.csproj", "{386A9769-59BF-4BE3-99D4-A9603E300729}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.JMESPath", "src\AWS.Lambda.Powertools.JMESPath\AWS.Lambda.Powertools.JMESPath.csproj", "{4F5020DB-9856-4A6F-B2CB-2C213FD749BC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.JMESPath.Tests", "tests\AWS.Lambda.Powertools.JMESPath.Tests\AWS.Lambda.Powertools.JMESPath.Tests.csproj", "{B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "e2e", "e2e", "{A19E6CBD-8078-49F9-849E-2E484BFAF324}" - ProjectSection(SolutionItems) = preProject - tests\e2e\Readme.md = tests\e2e\Readme.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infra", "Infra", "{93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Functions", "Functions", "{CDAE55EB-9438-4F54-B7ED-931D64324D5F}" - ProjectSection(SolutionItems) = preProject - tests\e2e\functions\payload.json = tests\e2e\functions\payload.json - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infra", "tests\e2e\infra\Infra.csproj", "{AA532674-A61C-41E6-8F9A-ED53D79AF1EC}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtils", "tests\e2e\functions\TestUtils\TestUtils.csproj", "{3C6162D7-0162-4BC2-BBF5-0554539A81CD}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Logging", "Logging", "{4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Metrics", "Metrics", "{BA495B95-C463-4759-AA9D-34C6614B3511}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tracing", "Tracing", "{3C9FA701-31FF-4747-B324-E0D252EAFD63}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function", "tests\e2e\functions\core\logging\Function\src\Function\Function.csproj", "{DEA5A48E-BC47-4E87-858C-282860CA196E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function.Tests", "tests\e2e\functions\core\logging\Function\test\Function.Tests\Function.Tests.csproj", "{45D90299-D29F-4380-8FE8-98DF70508290}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-Function", "tests\e2e\functions\core\logging\AOT-Function\src\AOT-Function\AOT-Function.csproj", "{E0DEFF37-3706-4F61-98CA-904E2F964605}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function", "tests\e2e\functions\core\metrics\Function\src\Function\Function.csproj", "{F858D526-7919-472B-8992-D627A4F3987C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function.Tests", "tests\e2e\functions\core\metrics\Function\test\Function.Tests\Function.Tests.csproj", "{92669F13-F9C9-479F-ABA1-163D93ABCF95}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-Function", "tests\e2e\functions\core\metrics\AOT-Function\src\AOT-Function\AOT-Function.csproj", "{CC30B576-10ED-4DB2-A6C7-3A603E671523}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function", "tests\e2e\functions\core\tracing\Function\src\Function\Function.csproj", "{D22576BE-3D83-47C1-8B35-947C0B6DA8ED}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function.Tests", "tests\e2e\functions\core\tracing\Function\test\Function.Tests\Function.Tests.csproj", "{8DDAFE37-ED59-4710-9415-8EBA44CC6437}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-Function", "tests\e2e\functions\core\tracing\AOT-Function\src\AOT-Function\AOT-Function.csproj", "{8DDED681-AE8D-45EB-A22E-2FFB88620F9B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfraAot", "tests\e2e\infra-aot\InfraAot.csproj", "{24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfraShared", "tests\e2e\InfraShared\InfraShared.csproj", "{D303B458-9D84-4DDF-8781-2C0211672329}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Idempotency", "Idempotency", "{FB2C7DA3-6FCE-429D-86F9-5775D0231EC6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function", "tests\e2e\functions\idempotency\Function\src\Function\Function.csproj", "{9AF99F6D-E8E7-443F-A965-D55B8E388836}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function.Tests", "tests\e2e\functions\idempotency\Function\test\Function.Tests\Function.Tests.csproj", "{FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-FunctionPayloadSubsetTest", "tests\e2e\functions\idempotency\AOT-Function\src\AOT-FunctionPayloadSubsetTest\AOT-FunctionPayloadSubsetTest.csproj", "{ACA789EA-BD38-490B-A7F8-6A3A86985025}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-FunctionHandlerTest", "tests\e2e\functions\idempotency\AOT-Function\src\AOT-FunctionHandlerTest\AOT-FunctionHandlerTest.csproj", "{E71C48D2-AD56-4177-BBD7-6BB859A40C92}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-FunctionMethodAttributeTest", "tests\e2e\functions\idempotency\AOT-Function\src\AOT-FunctionMethodAttributeTest\AOT-FunctionMethodAttributeTest.csproj", "{CC8CFF43-DC72-464C-A42D-55E023DE8500}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metrics.AspNetCore", "src\AWS.Lambda.Powertools.Metrics.AspNetCore\AWS.Lambda.Powertools.Metrics.AspNetCore.csproj", "{A2AD98B1-2BED-4864-B573-77BE7B52FED2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metrics.AspNetCore.Tests", "tests\AWS.Lambda.Powertools.Metrics.AspNetCore.Tests\AWS.Lambda.Powertools.Metrics.AspNetCore.Tests.csproj", "{F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Metrics", "Metrics", "{A566F2D7-F8FE-466A-8306-85F266B7E656}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-Function-ILogger", "tests\e2e\functions\core\logging\AOT-Function-ILogger\src\AOT-Function-ILogger\AOT-Function-ILogger.csproj", "{7FC6DD65-0352-4139-8D08-B25C0A0403E3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.EventHandler.Tests", "tests\AWS.Lambda.Powertools.EventHandler.Tests\AWS.Lambda.Powertools.EventHandler.Tests.csproj", "{61374D8E-F77C-4A31-AE07-35DAF1847369}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.EventHandler", "src\AWS.Lambda.Powertools.EventHandler\AWS.Lambda.Powertools.EventHandler.csproj", "{F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction", "src\AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction\AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction.csproj", "{281F7EB5-ACE5-458F-BC88-46A8899DF3BA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction.AspNetCore", "src\AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction.AspNetCore\AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction.AspNetCore.csproj", "{8A22F22E-D10A-4897-A89A-DC76C267F6BB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka", "src\AWS.Lambda.Powertools.Kafka\AWS.Lambda.Powertools.Kafka.csproj", "{5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka.Tests", "tests\AWS.Lambda.Powertools.Kafka.Tests\AWS.Lambda.Powertools.Kafka.Tests.csproj", "{FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka.Avro", "src\AWS.Lambda.Powertools.Kafka.Avro\AWS.Lambda.Powertools.Kafka.Avro.csproj", "{25F0929B-2E04-4ED6-A0ED-5379A0A755B0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka.Json", "src\AWS.Lambda.Powertools.Kafka.Json\AWS.Lambda.Powertools.Kafka.Json.csproj", "{9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka.Protobuf", "src\AWS.Lambda.Powertools.Kafka.Protobuf\AWS.Lambda.Powertools.Kafka.Protobuf.csproj", "{B640DB80-C982-407B-A2EC-CD29AC77DDB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.ModuleInitializer.Tests", "tests\AWS.Lambda.Powertools.ModuleInitializer.Tests\AWS.Lambda.Powertools.ModuleInitializer.Tests.csproj", "{E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.ConcurrencyTests", "tests\AWS.Lambda.Powertools.ConcurrencyTests\AWS.Lambda.Powertools.ConcurrencyTests.csproj", "{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}" -EndProject - -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|x64.ActiveCfg = Debug|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|x64.Build.0 = Debug|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|x86.ActiveCfg = Debug|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|x86.Build.0 = Debug|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|Any CPU.Build.0 = Release|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|x64.ActiveCfg = Release|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|x64.Build.0 = Release|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|x86.ActiveCfg = Release|Any CPU - {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|x86.Build.0 = Release|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|x64.ActiveCfg = Debug|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|x64.Build.0 = Debug|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|x86.ActiveCfg = Debug|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|x86.Build.0 = Debug|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|Any CPU.Build.0 = Release|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|x64.ActiveCfg = Release|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|x64.Build.0 = Release|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|x86.ActiveCfg = Release|Any CPU - {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|x86.Build.0 = Release|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|x64.ActiveCfg = Debug|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|x64.Build.0 = Debug|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|x86.ActiveCfg = Debug|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|x86.Build.0 = Debug|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|Any CPU.Build.0 = Release|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|x64.ActiveCfg = Release|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|x64.Build.0 = Release|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|x86.ActiveCfg = Release|Any CPU - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|x86.Build.0 = Release|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|x64.ActiveCfg = Debug|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|x64.Build.0 = Debug|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|x86.ActiveCfg = Debug|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|x86.Build.0 = Debug|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|Any CPU.Build.0 = Release|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|x64.ActiveCfg = Release|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|x64.Build.0 = Release|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|x86.ActiveCfg = Release|Any CPU - {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|x86.Build.0 = Release|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|x64.ActiveCfg = Debug|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|x64.Build.0 = Debug|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|x86.ActiveCfg = Debug|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|x86.Build.0 = Debug|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|Any CPU.Build.0 = Release|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|x64.ActiveCfg = Release|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|x64.Build.0 = Release|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|x86.ActiveCfg = Release|Any CPU - {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|x86.Build.0 = Release|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|x64.ActiveCfg = Debug|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|x64.Build.0 = Debug|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|x86.ActiveCfg = Debug|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|x86.Build.0 = Debug|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|Any CPU.Build.0 = Release|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|x64.ActiveCfg = Release|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|x64.Build.0 = Release|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|x86.ActiveCfg = Release|Any CPU - {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|x86.Build.0 = Release|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|x64.ActiveCfg = Debug|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|x64.Build.0 = Debug|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|x86.ActiveCfg = Debug|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|x86.Build.0 = Debug|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|Any CPU.Build.0 = Release|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|x64.ActiveCfg = Release|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|x64.Build.0 = Release|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|x86.ActiveCfg = Release|Any CPU - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|x86.Build.0 = Release|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x64.ActiveCfg = Debug|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x64.Build.0 = Debug|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x86.ActiveCfg = Debug|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x86.Build.0 = Debug|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|Any CPU.Build.0 = Release|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x64.ActiveCfg = Release|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x64.Build.0 = Release|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x86.ActiveCfg = Release|Any CPU - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x86.Build.0 = Release|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|x64.ActiveCfg = Debug|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|x64.Build.0 = Debug|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|x86.ActiveCfg = Debug|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|x86.Build.0 = Debug|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|Any CPU.Build.0 = Release|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|x64.ActiveCfg = Release|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|x64.Build.0 = Release|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|x86.ActiveCfg = Release|Any CPU - {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|x86.Build.0 = Release|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|x64.ActiveCfg = Debug|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|x64.Build.0 = Debug|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|x86.ActiveCfg = Debug|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|x86.Build.0 = Debug|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|Any CPU.Build.0 = Release|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|x64.ActiveCfg = Release|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|x64.Build.0 = Release|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|x86.ActiveCfg = Release|Any CPU - {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|x86.Build.0 = Release|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|x64.ActiveCfg = Debug|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|x64.Build.0 = Debug|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|x86.ActiveCfg = Debug|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|x86.Build.0 = Debug|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|Any CPU.Build.0 = Release|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|x64.ActiveCfg = Release|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|x64.Build.0 = Release|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|x86.ActiveCfg = Release|Any CPU - {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|x86.Build.0 = Release|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|x64.ActiveCfg = Debug|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|x64.Build.0 = Debug|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|x86.ActiveCfg = Debug|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|x86.Build.0 = Debug|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|Any CPU.Build.0 = Release|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|x64.ActiveCfg = Release|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|x64.Build.0 = Release|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|x86.ActiveCfg = Release|Any CPU - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|x86.Build.0 = Release|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|x64.ActiveCfg = Debug|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|x64.Build.0 = Debug|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|x86.ActiveCfg = Debug|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|x86.Build.0 = Debug|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|Any CPU.Build.0 = Release|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|x64.ActiveCfg = Release|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|x64.Build.0 = Release|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|x86.ActiveCfg = Release|Any CPU - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|x86.Build.0 = Release|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|Any CPU.Build.0 = Debug|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|x64.ActiveCfg = Debug|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|x64.Build.0 = Debug|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|x86.ActiveCfg = Debug|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|x86.Build.0 = Debug|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|Any CPU.ActiveCfg = Release|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|Any CPU.Build.0 = Release|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x64.ActiveCfg = Release|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x64.Build.0 = Release|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x86.ActiveCfg = Release|Any CPU - {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x86.Build.0 = Release|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x64.ActiveCfg = Debug|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x64.Build.0 = Debug|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x86.ActiveCfg = Debug|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x86.Build.0 = Debug|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|Any CPU.Build.0 = Release|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x64.ActiveCfg = Release|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x64.Build.0 = Release|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x86.ActiveCfg = Release|Any CPU - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x86.Build.0 = Release|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x64.ActiveCfg = Debug|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x64.Build.0 = Debug|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x86.ActiveCfg = Debug|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x86.Build.0 = Debug|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|Any CPU.Build.0 = Release|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x64.ActiveCfg = Release|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x64.Build.0 = Release|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x86.ActiveCfg = Release|Any CPU - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x86.Build.0 = Release|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|x64.ActiveCfg = Debug|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|x64.Build.0 = Debug|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|x86.ActiveCfg = Debug|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|x86.Build.0 = Debug|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|Any CPU.Build.0 = Release|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|x64.ActiveCfg = Release|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|x64.Build.0 = Release|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|x86.ActiveCfg = Release|Any CPU - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|x86.Build.0 = Release|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|x64.ActiveCfg = Debug|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|x64.Build.0 = Debug|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|x86.ActiveCfg = Debug|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|x86.Build.0 = Debug|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|Any CPU.Build.0 = Release|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|x64.ActiveCfg = Release|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|x64.Build.0 = Release|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|x86.ActiveCfg = Release|Any CPU - {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|x86.Build.0 = Release|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|x64.ActiveCfg = Debug|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|x64.Build.0 = Debug|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|x86.ActiveCfg = Debug|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|x86.Build.0 = Debug|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|Any CPU.Build.0 = Release|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|x64.ActiveCfg = Release|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|x64.Build.0 = Release|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|x86.ActiveCfg = Release|Any CPU - {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|x86.Build.0 = Release|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|Any CPU.Build.0 = Debug|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|x64.ActiveCfg = Debug|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|x64.Build.0 = Debug|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|x86.ActiveCfg = Debug|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|x86.Build.0 = Debug|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Release|Any CPU.ActiveCfg = Release|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Release|Any CPU.Build.0 = Release|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Release|x64.ActiveCfg = Release|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Release|x64.Build.0 = Release|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Release|x86.ActiveCfg = Release|Any CPU - {45D90299-D29F-4380-8FE8-98DF70508290}.Release|x86.Build.0 = Release|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|x64.ActiveCfg = Debug|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|x64.Build.0 = Debug|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|x86.ActiveCfg = Debug|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|x86.Build.0 = Debug|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|Any CPU.Build.0 = Release|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|x64.ActiveCfg = Release|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|x64.Build.0 = Release|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|x86.ActiveCfg = Release|Any CPU - {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|x86.Build.0 = Release|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Debug|x64.ActiveCfg = Debug|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Debug|x64.Build.0 = Debug|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Debug|x86.ActiveCfg = Debug|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Debug|x86.Build.0 = Debug|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Release|Any CPU.Build.0 = Release|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Release|x64.ActiveCfg = Release|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Release|x64.Build.0 = Release|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Release|x86.ActiveCfg = Release|Any CPU - {F858D526-7919-472B-8992-D627A4F3987C}.Release|x86.Build.0 = Release|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|x64.ActiveCfg = Debug|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|x64.Build.0 = Debug|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|x86.ActiveCfg = Debug|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|x86.Build.0 = Debug|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|Any CPU.Build.0 = Release|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|x64.ActiveCfg = Release|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|x64.Build.0 = Release|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|x86.ActiveCfg = Release|Any CPU - {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|x86.Build.0 = Release|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|x64.ActiveCfg = Debug|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|x64.Build.0 = Debug|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|x86.ActiveCfg = Debug|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|x86.Build.0 = Debug|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|Any CPU.Build.0 = Release|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|x64.ActiveCfg = Release|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|x64.Build.0 = Release|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|x86.ActiveCfg = Release|Any CPU - {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|x86.Build.0 = Release|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|x64.ActiveCfg = Debug|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|x64.Build.0 = Debug|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|x86.ActiveCfg = Debug|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|x86.Build.0 = Debug|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|Any CPU.Build.0 = Release|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|x64.ActiveCfg = Release|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|x64.Build.0 = Release|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|x86.ActiveCfg = Release|Any CPU - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|x86.Build.0 = Release|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|x64.ActiveCfg = Debug|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|x64.Build.0 = Debug|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|x86.ActiveCfg = Debug|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|x86.Build.0 = Debug|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|Any CPU.Build.0 = Release|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|x64.ActiveCfg = Release|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|x64.Build.0 = Release|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|x86.ActiveCfg = Release|Any CPU - {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|x86.Build.0 = Release|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|x64.ActiveCfg = Debug|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|x64.Build.0 = Debug|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|x86.ActiveCfg = Debug|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|x86.Build.0 = Debug|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|Any CPU.Build.0 = Release|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|x64.ActiveCfg = Release|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|x64.Build.0 = Release|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|x86.ActiveCfg = Release|Any CPU - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|x86.Build.0 = Release|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|x64.ActiveCfg = Debug|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|x64.Build.0 = Debug|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|x86.ActiveCfg = Debug|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|x86.Build.0 = Debug|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|Any CPU.Build.0 = Release|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x64.ActiveCfg = Release|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x64.Build.0 = Release|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x86.ActiveCfg = Release|Any CPU - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x86.Build.0 = Release|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x64.ActiveCfg = Debug|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x64.Build.0 = Debug|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x86.ActiveCfg = Debug|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x86.Build.0 = Debug|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Release|Any CPU.Build.0 = Release|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x64.ActiveCfg = Release|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x64.Build.0 = Release|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x86.ActiveCfg = Release|Any CPU - {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x86.Build.0 = Release|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x64.ActiveCfg = Debug|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x64.Build.0 = Debug|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x86.ActiveCfg = Debug|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x86.Build.0 = Debug|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|Any CPU.Build.0 = Release|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x64.ActiveCfg = Release|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x64.Build.0 = Release|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x86.ActiveCfg = Release|Any CPU - {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x86.Build.0 = Release|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x64.ActiveCfg = Debug|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x64.Build.0 = Debug|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x86.ActiveCfg = Debug|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x86.Build.0 = Debug|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|Any CPU.Build.0 = Release|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x64.ActiveCfg = Release|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x64.Build.0 = Release|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x86.ActiveCfg = Release|Any CPU - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x86.Build.0 = Release|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|x64.ActiveCfg = Debug|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|x64.Build.0 = Debug|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|x86.ActiveCfg = Debug|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|x86.Build.0 = Debug|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|Any CPU.Build.0 = Release|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|x64.ActiveCfg = Release|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|x64.Build.0 = Release|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|x86.ActiveCfg = Release|Any CPU - {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|x86.Build.0 = Release|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|x64.ActiveCfg = Debug|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|x64.Build.0 = Debug|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|x86.ActiveCfg = Debug|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|x86.Build.0 = Debug|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|Any CPU.Build.0 = Release|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|x64.ActiveCfg = Release|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|x64.Build.0 = Release|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|x86.ActiveCfg = Release|Any CPU - {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|x86.Build.0 = Release|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|x64.ActiveCfg = Debug|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|x64.Build.0 = Debug|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|x86.ActiveCfg = Debug|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|x86.Build.0 = Debug|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|Any CPU.Build.0 = Release|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x64.ActiveCfg = Release|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x64.Build.0 = Release|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x86.ActiveCfg = Release|Any CPU - {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x86.Build.0 = Release|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x64.ActiveCfg = Debug|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x64.Build.0 = Debug|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x86.ActiveCfg = Debug|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x86.Build.0 = Debug|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|Any CPU.Build.0 = Release|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x64.ActiveCfg = Release|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x64.Build.0 = Release|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x86.ActiveCfg = Release|Any CPU - {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x86.Build.0 = Release|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x64.ActiveCfg = Debug|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x64.Build.0 = Debug|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x86.ActiveCfg = Debug|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x86.Build.0 = Debug|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|Any CPU.Build.0 = Release|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x64.ActiveCfg = Release|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x64.Build.0 = Release|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x86.ActiveCfg = Release|Any CPU - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x86.Build.0 = Release|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|x64.ActiveCfg = Debug|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|x64.Build.0 = Debug|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|x86.ActiveCfg = Debug|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|x86.Build.0 = Debug|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|Any CPU.Build.0 = Release|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|x64.ActiveCfg = Release|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|x64.Build.0 = Release|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|x86.ActiveCfg = Release|Any CPU - {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|x86.Build.0 = Release|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|Any CPU.Build.0 = Debug|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|x64.ActiveCfg = Debug|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|x64.Build.0 = Debug|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|x86.ActiveCfg = Debug|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|x86.Build.0 = Debug|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|Any CPU.ActiveCfg = Release|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|Any CPU.Build.0 = Release|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|x64.ActiveCfg = Release|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|x64.Build.0 = Release|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|x86.ActiveCfg = Release|Any CPU - {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|x86.Build.0 = Release|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|x64.ActiveCfg = Debug|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|x64.Build.0 = Debug|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|x86.ActiveCfg = Debug|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|x86.Build.0 = Debug|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|Any CPU.Build.0 = Release|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|x64.ActiveCfg = Release|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|x64.Build.0 = Release|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|x86.ActiveCfg = Release|Any CPU - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|x86.Build.0 = Release|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|x64.ActiveCfg = Debug|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|x64.Build.0 = Debug|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|x86.ActiveCfg = Debug|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|x86.Build.0 = Debug|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|Any CPU.Build.0 = Release|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|x64.ActiveCfg = Release|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|x64.Build.0 = Release|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|x86.ActiveCfg = Release|Any CPU - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|x86.Build.0 = Release|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|x64.ActiveCfg = Debug|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|x64.Build.0 = Debug|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|x86.ActiveCfg = Debug|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|x86.Build.0 = Debug|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|Any CPU.Build.0 = Release|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|x64.ActiveCfg = Release|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|x64.Build.0 = Release|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|x86.ActiveCfg = Release|Any CPU - {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|x86.Build.0 = Release|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|x64.ActiveCfg = Debug|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|x64.Build.0 = Debug|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|x86.ActiveCfg = Debug|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|x86.Build.0 = Debug|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|Any CPU.Build.0 = Release|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|x64.ActiveCfg = Release|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|x64.Build.0 = Release|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|x86.ActiveCfg = Release|Any CPU - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|x86.Build.0 = Release|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|x64.ActiveCfg = Debug|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|x64.Build.0 = Debug|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|x86.ActiveCfg = Debug|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|x86.Build.0 = Debug|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|Any CPU.Build.0 = Release|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|x64.ActiveCfg = Release|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|x64.Build.0 = Release|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|x86.ActiveCfg = Release|Any CPU - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|x86.Build.0 = Release|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|x64.ActiveCfg = Debug|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|x64.Build.0 = Debug|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|x86.ActiveCfg = Debug|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|x86.Build.0 = Debug|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|Any CPU.Build.0 = Release|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|x64.ActiveCfg = Release|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|x64.Build.0 = Release|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|x86.ActiveCfg = Release|Any CPU - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|x86.Build.0 = Release|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|x64.ActiveCfg = Debug|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|x64.Build.0 = Debug|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|x86.ActiveCfg = Debug|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|x86.Build.0 = Debug|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|Any CPU.Build.0 = Release|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|x64.ActiveCfg = Release|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|x64.Build.0 = Release|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|x86.ActiveCfg = Release|Any CPU - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|x86.Build.0 = Release|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|x64.ActiveCfg = Debug|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|x64.Build.0 = Debug|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|x86.ActiveCfg = Debug|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|x86.Build.0 = Debug|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|Any CPU.Build.0 = Release|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|x64.ActiveCfg = Release|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|x64.Build.0 = Release|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|x86.ActiveCfg = Release|Any CPU - {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|x86.Build.0 = Release|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|x64.ActiveCfg = Debug|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|x64.Build.0 = Debug|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|x86.ActiveCfg = Debug|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|x86.Build.0 = Debug|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|Any CPU.Build.0 = Release|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|x64.ActiveCfg = Release|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|x64.Build.0 = Release|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|x86.ActiveCfg = Release|Any CPU - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|x86.Build.0 = Release|Any CPU - - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x64.ActiveCfg = Debug|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x64.Build.0 = Debug|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x86.ActiveCfg = Debug|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x86.Build.0 = Debug|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|Any CPU.Build.0 = Release|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x64.ActiveCfg = Release|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x64.Build.0 = Release|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x86.ActiveCfg = Release|Any CPU - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - - GlobalSection(NestedProjects) = preSolution - {ACED222F-BDEB-48B6-BA6E-A28659080766} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {7A6A2996-1F41-4C94-A0A2-3AE963247243} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {3BA6251D-DE4E-4547-AAA9-25F4BA04C636} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {1A3AC28C-3AEE-40FE-B229-9E38BB609547} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {B68A0D0A-4785-48CB-864F-29E3A8ACA526} = {1CFF5568-8486-475F-81F6-06105C437528} - {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E} = {1CFF5568-8486-475F-81F6-06105C437528} - {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5} = {1CFF5568-8486-475F-81F6-06105C437528} - {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {386A9769-59BF-4BE3-99D4-A9603E300729} = {1CFF5568-8486-475F-81F6-06105C437528} - {B7AC87DF-9705-47D9-AC00-C230E577CA5D} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {F8B4100F-4014-4A1E-8130-D281453B79ED} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {12B940EF-A5D3-459D-BD36-A603834D1F7D} = {1CFF5568-8486-475F-81F6-06105C437528} - {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3} = {1CFF5568-8486-475F-81F6-06105C437528} - {4F5020DB-9856-4A6F-B2CB-2C213FD749BC} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1} = {1CFF5568-8486-475F-81F6-06105C437528} - {A19E6CBD-8078-49F9-849E-2E484BFAF324} = {1CFF5568-8486-475F-81F6-06105C437528} - {93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7} = {A19E6CBD-8078-49F9-849E-2E484BFAF324} - {CDAE55EB-9438-4F54-B7ED-931D64324D5F} = {A19E6CBD-8078-49F9-849E-2E484BFAF324} - {AA532674-A61C-41E6-8F9A-ED53D79AF1EC} = {93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7} - {AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0} = {CDAE55EB-9438-4F54-B7ED-931D64324D5F} - {3C6162D7-0162-4BC2-BBF5-0554539A81CD} = {CDAE55EB-9438-4F54-B7ED-931D64324D5F} - {4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F} = {AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0} - {BA495B95-C463-4759-AA9D-34C6614B3511} = {AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0} - {3C9FA701-31FF-4747-B324-E0D252EAFD63} = {AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0} - {DEA5A48E-BC47-4E87-858C-282860CA196E} = {4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F} - {45D90299-D29F-4380-8FE8-98DF70508290} = {4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F} - {E0DEFF37-3706-4F61-98CA-904E2F964605} = {4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F} - {F858D526-7919-472B-8992-D627A4F3987C} = {BA495B95-C463-4759-AA9D-34C6614B3511} - {92669F13-F9C9-479F-ABA1-163D93ABCF95} = {BA495B95-C463-4759-AA9D-34C6614B3511} - {CC30B576-10ED-4DB2-A6C7-3A603E671523} = {BA495B95-C463-4759-AA9D-34C6614B3511} - {D22576BE-3D83-47C1-8B35-947C0B6DA8ED} = {3C9FA701-31FF-4747-B324-E0D252EAFD63} - {8DDAFE37-ED59-4710-9415-8EBA44CC6437} = {3C9FA701-31FF-4747-B324-E0D252EAFD63} - {8DDED681-AE8D-45EB-A22E-2FFB88620F9B} = {3C9FA701-31FF-4747-B324-E0D252EAFD63} - {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95} = {93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7} - {D303B458-9D84-4DDF-8781-2C0211672329} = {93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7} - {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} = {CDAE55EB-9438-4F54-B7ED-931D64324D5F} - {9AF99F6D-E8E7-443F-A965-D55B8E388836} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} - {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} - {ACA789EA-BD38-490B-A7F8-6A3A86985025} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} - {E71C48D2-AD56-4177-BBD7-6BB859A40C92} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} - {CC8CFF43-DC72-464C-A42D-55E023DE8500} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} - {A2AD98B1-2BED-4864-B573-77BE7B52FED2} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {A566F2D7-F8FE-466A-8306-85F266B7E656} = {1CFF5568-8486-475F-81F6-06105C437528} - {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB} = {A566F2D7-F8FE-466A-8306-85F266B7E656} - {A422C742-2CF9-409D-BDAE-15825AB62113} = {A566F2D7-F8FE-466A-8306-85F266B7E656} - {7FC6DD65-0352-4139-8D08-B25C0A0403E3} = {4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F} - {61374D8E-F77C-4A31-AE07-35DAF1847369} = {1CFF5568-8486-475F-81F6-06105C437528} - {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {281F7EB5-ACE5-458F-BC88-46A8899DF3BA} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {8A22F22E-D10A-4897-A89A-DC76C267F6BB} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645} = {1CFF5568-8486-475F-81F6-06105C437528} - {25F0929B-2E04-4ED6-A0ED-5379A0A755B0} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {B640DB80-C982-407B-A2EC-CD29AC77DDB8} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} - {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B} = {1CFF5568-8486-475F-81F6-06105C437528} - - {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5} = {1CFF5568-8486-475F-81F6-06105C437528} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32929.385 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{73C9B1E5-3893-47E8-B373-17E5F5D7E6F5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Common", "src\AWS.Lambda.Powertools.Common\AWS.Lambda.Powertools.Common.csproj", "{ACED222F-BDEB-48B6-BA6E-A28659080766}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Logging", "src\AWS.Lambda.Powertools.Logging\AWS.Lambda.Powertools.Logging.csproj", "{7A6A2996-1F41-4C94-A0A2-3AE963247243}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metrics", "src\AWS.Lambda.Powertools.Metrics\AWS.Lambda.Powertools.Metrics.csproj", "{3BA6251D-DE4E-4547-AAA9-25F4BA04C636}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Tracing", "src\AWS.Lambda.Powertools.Tracing\AWS.Lambda.Powertools.Tracing.csproj", "{1A3AC28C-3AEE-40FE-B229-9E38BB609547}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1CFF5568-8486-475F-81F6-06105C437528}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Logging.Tests", "tests\AWS.Lambda.Powertools.Logging.Tests\AWS.Lambda.Powertools.Logging.Tests.csproj", "{B68A0D0A-4785-48CB-864F-29E3A8ACA526}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metrics.Tests", "tests\AWS.Lambda.Powertools.Metrics.Tests\AWS.Lambda.Powertools.Metrics.Tests.csproj", "{A422C742-2CF9-409D-BDAE-15825AB62113}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Tracing.Tests", "tests\AWS.Lambda.Powertools.Tracing.Tests\AWS.Lambda.Powertools.Tracing.Tests.csproj", "{A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Common.Tests", "tests\AWS.Lambda.Powertools.Common.Tests\AWS.Lambda.Powertools.Common.Tests.csproj", "{4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.BatchProcessing", "src\AWS.Lambda.Powertools.BatchProcessing\AWS.Lambda.Powertools.BatchProcessing.csproj", "{F8B4100F-4014-4A1E-8130-D281453B79ED}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.BatchProcessing.Tests", "tests\AWS.Lambda.Powertools.BatchProcessing.Tests\AWS.Lambda.Powertools.BatchProcessing.Tests.csproj", "{12B940EF-A5D3-459D-BD36-A603834D1F7D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Idempotency", "src\AWS.Lambda.Powertools.Idempotency\AWS.Lambda.Powertools.Idempotency.csproj", "{B7AC87DF-9705-47D9-AC00-C230E577CA5D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Idempotency.Tests", "tests\AWS.Lambda.Powertools.Idempotency.Tests\AWS.Lambda.Powertools.Idempotency.Tests.csproj", "{3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Parameters", "src\AWS.Lambda.Powertools.Parameters\AWS.Lambda.Powertools.Parameters.csproj", "{1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Parameters.Tests", "tests\AWS.Lambda.Powertools.Parameters.Tests\AWS.Lambda.Powertools.Parameters.Tests.csproj", "{386A9769-59BF-4BE3-99D4-A9603E300729}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.JMESPath", "src\AWS.Lambda.Powertools.JMESPath\AWS.Lambda.Powertools.JMESPath.csproj", "{4F5020DB-9856-4A6F-B2CB-2C213FD749BC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.JMESPath.Tests", "tests\AWS.Lambda.Powertools.JMESPath.Tests\AWS.Lambda.Powertools.JMESPath.Tests.csproj", "{B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "e2e", "e2e", "{A19E6CBD-8078-49F9-849E-2E484BFAF324}" + ProjectSection(SolutionItems) = preProject + tests\e2e\Readme.md = tests\e2e\Readme.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infra", "Infra", "{93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Functions", "Functions", "{CDAE55EB-9438-4F54-B7ED-931D64324D5F}" + ProjectSection(SolutionItems) = preProject + tests\e2e\functions\payload.json = tests\e2e\functions\payload.json + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infra", "tests\e2e\infra\Infra.csproj", "{AA532674-A61C-41E6-8F9A-ED53D79AF1EC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtils", "tests\e2e\functions\TestUtils\TestUtils.csproj", "{3C6162D7-0162-4BC2-BBF5-0554539A81CD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Logging", "Logging", "{4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Metrics", "Metrics", "{BA495B95-C463-4759-AA9D-34C6614B3511}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tracing", "Tracing", "{3C9FA701-31FF-4747-B324-E0D252EAFD63}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function", "tests\e2e\functions\core\logging\Function\src\Function\Function.csproj", "{DEA5A48E-BC47-4E87-858C-282860CA196E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function.Tests", "tests\e2e\functions\core\logging\Function\test\Function.Tests\Function.Tests.csproj", "{45D90299-D29F-4380-8FE8-98DF70508290}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-Function", "tests\e2e\functions\core\logging\AOT-Function\src\AOT-Function\AOT-Function.csproj", "{E0DEFF37-3706-4F61-98CA-904E2F964605}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function", "tests\e2e\functions\core\metrics\Function\src\Function\Function.csproj", "{F858D526-7919-472B-8992-D627A4F3987C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function.Tests", "tests\e2e\functions\core\metrics\Function\test\Function.Tests\Function.Tests.csproj", "{92669F13-F9C9-479F-ABA1-163D93ABCF95}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-Function", "tests\e2e\functions\core\metrics\AOT-Function\src\AOT-Function\AOT-Function.csproj", "{CC30B576-10ED-4DB2-A6C7-3A603E671523}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function", "tests\e2e\functions\core\tracing\Function\src\Function\Function.csproj", "{D22576BE-3D83-47C1-8B35-947C0B6DA8ED}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function.Tests", "tests\e2e\functions\core\tracing\Function\test\Function.Tests\Function.Tests.csproj", "{8DDAFE37-ED59-4710-9415-8EBA44CC6437}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-Function", "tests\e2e\functions\core\tracing\AOT-Function\src\AOT-Function\AOT-Function.csproj", "{8DDED681-AE8D-45EB-A22E-2FFB88620F9B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfraAot", "tests\e2e\infra-aot\InfraAot.csproj", "{24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfraShared", "tests\e2e\InfraShared\InfraShared.csproj", "{D303B458-9D84-4DDF-8781-2C0211672329}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Idempotency", "Idempotency", "{FB2C7DA3-6FCE-429D-86F9-5775D0231EC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function", "tests\e2e\functions\idempotency\Function\src\Function\Function.csproj", "{9AF99F6D-E8E7-443F-A965-D55B8E388836}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function.Tests", "tests\e2e\functions\idempotency\Function\test\Function.Tests\Function.Tests.csproj", "{FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-FunctionPayloadSubsetTest", "tests\e2e\functions\idempotency\AOT-Function\src\AOT-FunctionPayloadSubsetTest\AOT-FunctionPayloadSubsetTest.csproj", "{ACA789EA-BD38-490B-A7F8-6A3A86985025}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-FunctionHandlerTest", "tests\e2e\functions\idempotency\AOT-Function\src\AOT-FunctionHandlerTest\AOT-FunctionHandlerTest.csproj", "{E71C48D2-AD56-4177-BBD7-6BB859A40C92}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-FunctionMethodAttributeTest", "tests\e2e\functions\idempotency\AOT-Function\src\AOT-FunctionMethodAttributeTest\AOT-FunctionMethodAttributeTest.csproj", "{CC8CFF43-DC72-464C-A42D-55E023DE8500}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metrics.AspNetCore", "src\AWS.Lambda.Powertools.Metrics.AspNetCore\AWS.Lambda.Powertools.Metrics.AspNetCore.csproj", "{A2AD98B1-2BED-4864-B573-77BE7B52FED2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metrics.AspNetCore.Tests", "tests\AWS.Lambda.Powertools.Metrics.AspNetCore.Tests\AWS.Lambda.Powertools.Metrics.AspNetCore.Tests.csproj", "{F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Metrics", "Metrics", "{A566F2D7-F8FE-466A-8306-85F266B7E656}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-Function-ILogger", "tests\e2e\functions\core\logging\AOT-Function-ILogger\src\AOT-Function-ILogger\AOT-Function-ILogger.csproj", "{7FC6DD65-0352-4139-8D08-B25C0A0403E3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.EventHandler.Tests", "tests\AWS.Lambda.Powertools.EventHandler.Tests\AWS.Lambda.Powertools.EventHandler.Tests.csproj", "{61374D8E-F77C-4A31-AE07-35DAF1847369}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.EventHandler", "src\AWS.Lambda.Powertools.EventHandler\AWS.Lambda.Powertools.EventHandler.csproj", "{F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction", "src\AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction\AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction.csproj", "{281F7EB5-ACE5-458F-BC88-46A8899DF3BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction.AspNetCore", "src\AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction.AspNetCore\AWS.Lambda.Powertools.EventHandler.Resolvers.BedrockAgentFunction.AspNetCore.csproj", "{8A22F22E-D10A-4897-A89A-DC76C267F6BB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka", "src\AWS.Lambda.Powertools.Kafka\AWS.Lambda.Powertools.Kafka.csproj", "{5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka.Tests", "tests\AWS.Lambda.Powertools.Kafka.Tests\AWS.Lambda.Powertools.Kafka.Tests.csproj", "{FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka.Avro", "src\AWS.Lambda.Powertools.Kafka.Avro\AWS.Lambda.Powertools.Kafka.Avro.csproj", "{25F0929B-2E04-4ED6-A0ED-5379A0A755B0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka.Json", "src\AWS.Lambda.Powertools.Kafka.Json\AWS.Lambda.Powertools.Kafka.Json.csproj", "{9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka.Protobuf", "src\AWS.Lambda.Powertools.Kafka.Protobuf\AWS.Lambda.Powertools.Kafka.Protobuf.csproj", "{B640DB80-C982-407B-A2EC-CD29AC77DDB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.ModuleInitializer.Tests", "tests\AWS.Lambda.Powertools.ModuleInitializer.Tests\AWS.Lambda.Powertools.ModuleInitializer.Tests.csproj", "{E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.ConcurrencyTests", "tests\AWS.Lambda.Powertools.ConcurrencyTests\AWS.Lambda.Powertools.ConcurrencyTests.csproj", "{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metadata", "src\AWS.Lambda.Powertools.Metadata\AWS.Lambda.Powertools.Metadata.csproj", "{6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metadata.Tests", "tests\AWS.Lambda.Powertools.Metadata.Tests\AWS.Lambda.Powertools.Metadata.Tests.csproj", "{B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|x64.ActiveCfg = Debug|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|x64.Build.0 = Debug|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|x86.ActiveCfg = Debug|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Debug|x86.Build.0 = Debug|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|Any CPU.Build.0 = Release|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|x64.ActiveCfg = Release|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|x64.Build.0 = Release|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|x86.ActiveCfg = Release|Any CPU + {ACED222F-BDEB-48B6-BA6E-A28659080766}.Release|x86.Build.0 = Release|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|x64.ActiveCfg = Debug|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|x64.Build.0 = Debug|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|x86.ActiveCfg = Debug|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Debug|x86.Build.0 = Debug|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|Any CPU.Build.0 = Release|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|x64.ActiveCfg = Release|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|x64.Build.0 = Release|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|x86.ActiveCfg = Release|Any CPU + {7A6A2996-1F41-4C94-A0A2-3AE963247243}.Release|x86.Build.0 = Release|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|x64.ActiveCfg = Debug|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|x64.Build.0 = Debug|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|x86.ActiveCfg = Debug|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Debug|x86.Build.0 = Debug|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|Any CPU.Build.0 = Release|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|x64.ActiveCfg = Release|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|x64.Build.0 = Release|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|x86.ActiveCfg = Release|Any CPU + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636}.Release|x86.Build.0 = Release|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|x64.ActiveCfg = Debug|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|x64.Build.0 = Debug|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|x86.ActiveCfg = Debug|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Debug|x86.Build.0 = Debug|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|Any CPU.Build.0 = Release|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|x64.ActiveCfg = Release|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|x64.Build.0 = Release|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|x86.ActiveCfg = Release|Any CPU + {1A3AC28C-3AEE-40FE-B229-9E38BB609547}.Release|x86.Build.0 = Release|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|x64.ActiveCfg = Debug|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|x64.Build.0 = Debug|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|x86.ActiveCfg = Debug|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Debug|x86.Build.0 = Debug|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|Any CPU.Build.0 = Release|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|x64.ActiveCfg = Release|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|x64.Build.0 = Release|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|x86.ActiveCfg = Release|Any CPU + {B68A0D0A-4785-48CB-864F-29E3A8ACA526}.Release|x86.Build.0 = Release|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|x64.ActiveCfg = Debug|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|x64.Build.0 = Debug|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|x86.ActiveCfg = Debug|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Debug|x86.Build.0 = Debug|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|Any CPU.Build.0 = Release|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|x64.ActiveCfg = Release|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|x64.Build.0 = Release|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|x86.ActiveCfg = Release|Any CPU + {A422C742-2CF9-409D-BDAE-15825AB62113}.Release|x86.Build.0 = Release|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|x64.ActiveCfg = Debug|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|x64.Build.0 = Debug|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|x86.ActiveCfg = Debug|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Debug|x86.Build.0 = Debug|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|Any CPU.Build.0 = Release|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|x64.ActiveCfg = Release|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|x64.Build.0 = Release|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|x86.ActiveCfg = Release|Any CPU + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|x86.Build.0 = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x64.ActiveCfg = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x64.Build.0 = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x86.ActiveCfg = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x86.Build.0 = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|Any CPU.Build.0 = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x64.ActiveCfg = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x64.Build.0 = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x86.ActiveCfg = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x86.Build.0 = Release|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|x64.ActiveCfg = Debug|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|x64.Build.0 = Debug|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|x86.ActiveCfg = Debug|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Debug|x86.Build.0 = Debug|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|Any CPU.Build.0 = Release|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|x64.ActiveCfg = Release|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|x64.Build.0 = Release|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|x86.ActiveCfg = Release|Any CPU + {F8B4100F-4014-4A1E-8130-D281453B79ED}.Release|x86.Build.0 = Release|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|x64.ActiveCfg = Debug|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|x64.Build.0 = Debug|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|x86.ActiveCfg = Debug|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Debug|x86.Build.0 = Debug|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|Any CPU.Build.0 = Release|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|x64.ActiveCfg = Release|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|x64.Build.0 = Release|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|x86.ActiveCfg = Release|Any CPU + {12B940EF-A5D3-459D-BD36-A603834D1F7D}.Release|x86.Build.0 = Release|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|x64.ActiveCfg = Debug|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|x64.Build.0 = Debug|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|x86.ActiveCfg = Debug|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Debug|x86.Build.0 = Debug|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|Any CPU.Build.0 = Release|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|x64.ActiveCfg = Release|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|x64.Build.0 = Release|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|x86.ActiveCfg = Release|Any CPU + {B7AC87DF-9705-47D9-AC00-C230E577CA5D}.Release|x86.Build.0 = Release|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|x64.ActiveCfg = Debug|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|x64.Build.0 = Debug|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Debug|x86.Build.0 = Debug|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|Any CPU.Build.0 = Release|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|x64.ActiveCfg = Release|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|x64.Build.0 = Release|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|x86.ActiveCfg = Release|Any CPU + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3}.Release|x86.Build.0 = Release|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|x64.ActiveCfg = Debug|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|x64.Build.0 = Debug|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|x86.ActiveCfg = Debug|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Debug|x86.Build.0 = Debug|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|Any CPU.Build.0 = Release|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|x64.ActiveCfg = Release|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|x64.Build.0 = Release|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|x86.ActiveCfg = Release|Any CPU + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0}.Release|x86.Build.0 = Release|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|Any CPU.Build.0 = Debug|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|x64.ActiveCfg = Debug|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|x64.Build.0 = Debug|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|x86.ActiveCfg = Debug|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Debug|x86.Build.0 = Debug|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|Any CPU.ActiveCfg = Release|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|Any CPU.Build.0 = Release|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x64.ActiveCfg = Release|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x64.Build.0 = Release|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x86.ActiveCfg = Release|Any CPU + {386A9769-59BF-4BE3-99D4-A9603E300729}.Release|x86.Build.0 = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x64.ActiveCfg = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x64.Build.0 = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x86.ActiveCfg = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Debug|x86.Build.0 = Debug|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|Any CPU.Build.0 = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x64.ActiveCfg = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x64.Build.0 = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x86.ActiveCfg = Release|Any CPU + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC}.Release|x86.Build.0 = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x64.ActiveCfg = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x64.Build.0 = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x86.ActiveCfg = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Debug|x86.Build.0 = Debug|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|Any CPU.Build.0 = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x64.ActiveCfg = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x64.Build.0 = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x86.ActiveCfg = Release|Any CPU + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1}.Release|x86.Build.0 = Release|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|x64.ActiveCfg = Debug|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|x64.Build.0 = Debug|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|x86.ActiveCfg = Debug|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Debug|x86.Build.0 = Debug|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|Any CPU.Build.0 = Release|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|x64.ActiveCfg = Release|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|x64.Build.0 = Release|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|x86.ActiveCfg = Release|Any CPU + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC}.Release|x86.Build.0 = Release|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|x64.ActiveCfg = Debug|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|x64.Build.0 = Debug|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|x86.ActiveCfg = Debug|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Debug|x86.Build.0 = Debug|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|Any CPU.Build.0 = Release|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|x64.ActiveCfg = Release|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|x64.Build.0 = Release|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|x86.ActiveCfg = Release|Any CPU + {3C6162D7-0162-4BC2-BBF5-0554539A81CD}.Release|x86.Build.0 = Release|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|x64.ActiveCfg = Debug|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|x64.Build.0 = Debug|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|x86.ActiveCfg = Debug|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Debug|x86.Build.0 = Debug|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|Any CPU.Build.0 = Release|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|x64.ActiveCfg = Release|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|x64.Build.0 = Release|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|x86.ActiveCfg = Release|Any CPU + {DEA5A48E-BC47-4E87-858C-282860CA196E}.Release|x86.Build.0 = Release|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|Any CPU.Build.0 = Debug|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|x64.ActiveCfg = Debug|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|x64.Build.0 = Debug|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|x86.ActiveCfg = Debug|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Debug|x86.Build.0 = Debug|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Release|Any CPU.ActiveCfg = Release|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Release|Any CPU.Build.0 = Release|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Release|x64.ActiveCfg = Release|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Release|x64.Build.0 = Release|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Release|x86.ActiveCfg = Release|Any CPU + {45D90299-D29F-4380-8FE8-98DF70508290}.Release|x86.Build.0 = Release|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|x64.ActiveCfg = Debug|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|x64.Build.0 = Debug|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|x86.ActiveCfg = Debug|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Debug|x86.Build.0 = Debug|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|Any CPU.Build.0 = Release|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|x64.ActiveCfg = Release|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|x64.Build.0 = Release|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|x86.ActiveCfg = Release|Any CPU + {E0DEFF37-3706-4F61-98CA-904E2F964605}.Release|x86.Build.0 = Release|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Debug|x64.ActiveCfg = Debug|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Debug|x64.Build.0 = Debug|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Debug|x86.ActiveCfg = Debug|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Debug|x86.Build.0 = Debug|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Release|Any CPU.Build.0 = Release|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Release|x64.ActiveCfg = Release|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Release|x64.Build.0 = Release|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Release|x86.ActiveCfg = Release|Any CPU + {F858D526-7919-472B-8992-D627A4F3987C}.Release|x86.Build.0 = Release|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|x64.ActiveCfg = Debug|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|x64.Build.0 = Debug|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|x86.ActiveCfg = Debug|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Debug|x86.Build.0 = Debug|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|Any CPU.Build.0 = Release|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|x64.ActiveCfg = Release|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|x64.Build.0 = Release|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|x86.ActiveCfg = Release|Any CPU + {92669F13-F9C9-479F-ABA1-163D93ABCF95}.Release|x86.Build.0 = Release|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|x64.ActiveCfg = Debug|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|x64.Build.0 = Debug|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Debug|x86.Build.0 = Debug|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|Any CPU.Build.0 = Release|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|x64.ActiveCfg = Release|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|x64.Build.0 = Release|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|x86.ActiveCfg = Release|Any CPU + {CC30B576-10ED-4DB2-A6C7-3A603E671523}.Release|x86.Build.0 = Release|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|x64.ActiveCfg = Debug|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|x64.Build.0 = Debug|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|x86.ActiveCfg = Debug|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Debug|x86.Build.0 = Debug|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|Any CPU.Build.0 = Release|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|x64.ActiveCfg = Release|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|x64.Build.0 = Release|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|x86.ActiveCfg = Release|Any CPU + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED}.Release|x86.Build.0 = Release|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|x64.ActiveCfg = Debug|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|x64.Build.0 = Debug|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|x86.ActiveCfg = Debug|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Debug|x86.Build.0 = Debug|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|Any CPU.Build.0 = Release|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|x64.ActiveCfg = Release|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|x64.Build.0 = Release|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|x86.ActiveCfg = Release|Any CPU + {8DDAFE37-ED59-4710-9415-8EBA44CC6437}.Release|x86.Build.0 = Release|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|x64.ActiveCfg = Debug|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|x64.Build.0 = Debug|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|x86.ActiveCfg = Debug|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Debug|x86.Build.0 = Debug|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|Any CPU.Build.0 = Release|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|x64.ActiveCfg = Release|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|x64.Build.0 = Release|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|x86.ActiveCfg = Release|Any CPU + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B}.Release|x86.Build.0 = Release|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|x64.ActiveCfg = Debug|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|x64.Build.0 = Debug|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|x86.ActiveCfg = Debug|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Debug|x86.Build.0 = Debug|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|Any CPU.Build.0 = Release|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x64.ActiveCfg = Release|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x64.Build.0 = Release|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x86.ActiveCfg = Release|Any CPU + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x86.Build.0 = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x64.ActiveCfg = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x64.Build.0 = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x86.ActiveCfg = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x86.Build.0 = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|Any CPU.Build.0 = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x64.ActiveCfg = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x64.Build.0 = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x86.ActiveCfg = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x86.Build.0 = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x64.ActiveCfg = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x64.Build.0 = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x86.ActiveCfg = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x86.Build.0 = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|Any CPU.Build.0 = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x64.ActiveCfg = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x64.Build.0 = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x86.ActiveCfg = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x86.Build.0 = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x64.Build.0 = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x86.Build.0 = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|Any CPU.Build.0 = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x64.ActiveCfg = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x64.Build.0 = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x86.ActiveCfg = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x86.Build.0 = Release|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|x64.ActiveCfg = Debug|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|x64.Build.0 = Debug|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|x86.ActiveCfg = Debug|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Debug|x86.Build.0 = Debug|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|Any CPU.Build.0 = Release|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|x64.ActiveCfg = Release|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|x64.Build.0 = Release|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|x86.ActiveCfg = Release|Any CPU + {ACA789EA-BD38-490B-A7F8-6A3A86985025}.Release|x86.Build.0 = Release|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|x64.ActiveCfg = Debug|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|x64.Build.0 = Debug|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|x86.ActiveCfg = Debug|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Debug|x86.Build.0 = Debug|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|Any CPU.Build.0 = Release|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|x64.ActiveCfg = Release|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|x64.Build.0 = Release|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|x86.ActiveCfg = Release|Any CPU + {E71C48D2-AD56-4177-BBD7-6BB859A40C92}.Release|x86.Build.0 = Release|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|x64.ActiveCfg = Debug|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|x64.Build.0 = Debug|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Debug|x86.Build.0 = Debug|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|Any CPU.Build.0 = Release|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x64.ActiveCfg = Release|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x64.Build.0 = Release|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x86.ActiveCfg = Release|Any CPU + {CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x86.Build.0 = Release|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x64.ActiveCfg = Debug|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x64.Build.0 = Debug|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x86.ActiveCfg = Debug|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x86.Build.0 = Debug|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|Any CPU.Build.0 = Release|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x64.ActiveCfg = Release|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x64.Build.0 = Release|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x86.ActiveCfg = Release|Any CPU + {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x86.Build.0 = Release|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x64.ActiveCfg = Debug|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x64.Build.0 = Debug|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x86.ActiveCfg = Debug|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x86.Build.0 = Debug|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|Any CPU.Build.0 = Release|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x64.ActiveCfg = Release|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x64.Build.0 = Release|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x86.ActiveCfg = Release|Any CPU + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x86.Build.0 = Release|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|x64.ActiveCfg = Debug|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|x64.Build.0 = Debug|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|x86.ActiveCfg = Debug|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Debug|x86.Build.0 = Debug|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|Any CPU.Build.0 = Release|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|x64.ActiveCfg = Release|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|x64.Build.0 = Release|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|x86.ActiveCfg = Release|Any CPU + {7FC6DD65-0352-4139-8D08-B25C0A0403E3}.Release|x86.Build.0 = Release|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|x64.ActiveCfg = Debug|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|x64.Build.0 = Debug|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|x86.ActiveCfg = Debug|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Debug|x86.Build.0 = Debug|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|Any CPU.Build.0 = Release|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|x64.ActiveCfg = Release|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|x64.Build.0 = Release|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|x86.ActiveCfg = Release|Any CPU + {61374D8E-F77C-4A31-AE07-35DAF1847369}.Release|x86.Build.0 = Release|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|x64.ActiveCfg = Debug|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|x64.Build.0 = Debug|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|x86.ActiveCfg = Debug|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Debug|x86.Build.0 = Debug|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|Any CPU.Build.0 = Release|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|x64.ActiveCfg = Release|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|x64.Build.0 = Release|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|x86.ActiveCfg = Release|Any CPU + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE}.Release|x86.Build.0 = Release|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|x64.Build.0 = Debug|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Debug|x86.Build.0 = Debug|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|Any CPU.Build.0 = Release|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|x64.ActiveCfg = Release|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|x64.Build.0 = Release|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|x86.ActiveCfg = Release|Any CPU + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA}.Release|x86.Build.0 = Release|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|x64.ActiveCfg = Debug|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|x64.Build.0 = Debug|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|x86.ActiveCfg = Debug|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Debug|x86.Build.0 = Debug|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|Any CPU.Build.0 = Release|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|x64.ActiveCfg = Release|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|x64.Build.0 = Release|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|x86.ActiveCfg = Release|Any CPU + {8A22F22E-D10A-4897-A89A-DC76C267F6BB}.Release|x86.Build.0 = Release|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|x64.ActiveCfg = Debug|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|x64.Build.0 = Debug|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|x86.ActiveCfg = Debug|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Debug|x86.Build.0 = Debug|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|Any CPU.Build.0 = Release|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|x64.ActiveCfg = Release|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|x64.Build.0 = Release|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|x86.ActiveCfg = Release|Any CPU + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3}.Release|x86.Build.0 = Release|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|x64.ActiveCfg = Debug|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|x64.Build.0 = Debug|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Debug|x86.Build.0 = Debug|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|Any CPU.Build.0 = Release|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|x64.ActiveCfg = Release|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|x64.Build.0 = Release|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|x86.ActiveCfg = Release|Any CPU + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645}.Release|x86.Build.0 = Release|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|x64.ActiveCfg = Debug|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|x64.Build.0 = Debug|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|x86.ActiveCfg = Debug|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Debug|x86.Build.0 = Debug|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|Any CPU.Build.0 = Release|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|x64.ActiveCfg = Release|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|x64.Build.0 = Release|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|x86.ActiveCfg = Release|Any CPU + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0}.Release|x86.Build.0 = Release|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|x64.ActiveCfg = Debug|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|x64.Build.0 = Debug|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|x86.ActiveCfg = Debug|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Debug|x86.Build.0 = Debug|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|Any CPU.Build.0 = Release|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|x64.ActiveCfg = Release|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|x64.Build.0 = Release|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|x86.ActiveCfg = Release|Any CPU + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E}.Release|x86.Build.0 = Release|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|x64.ActiveCfg = Debug|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|x64.Build.0 = Debug|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|x86.ActiveCfg = Debug|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Debug|x86.Build.0 = Debug|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|Any CPU.Build.0 = Release|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|x64.ActiveCfg = Release|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|x64.Build.0 = Release|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|x86.ActiveCfg = Release|Any CPU + {B640DB80-C982-407B-A2EC-CD29AC77DDB8}.Release|x86.Build.0 = Release|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|x64.ActiveCfg = Debug|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|x64.Build.0 = Debug|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|x86.ActiveCfg = Debug|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Debug|x86.Build.0 = Debug|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|Any CPU.Build.0 = Release|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|x64.ActiveCfg = Release|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|x64.Build.0 = Release|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|x86.ActiveCfg = Release|Any CPU + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|x86.Build.0 = Release|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x64.ActiveCfg = Debug|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x64.Build.0 = Debug|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x86.ActiveCfg = Debug|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x86.Build.0 = Debug|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|Any CPU.Build.0 = Release|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x64.ActiveCfg = Release|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x64.Build.0 = Release|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x86.ActiveCfg = Release|Any CPU + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x86.Build.0 = Release|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Debug|x64.ActiveCfg = Debug|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Debug|x64.Build.0 = Debug|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Debug|x86.ActiveCfg = Debug|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Debug|x86.Build.0 = Debug|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Release|Any CPU.Build.0 = Release|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Release|x64.ActiveCfg = Release|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Release|x64.Build.0 = Release|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Release|x86.ActiveCfg = Release|Any CPU + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0}.Release|x86.Build.0 = Release|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Debug|x64.ActiveCfg = Debug|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Debug|x64.Build.0 = Debug|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Debug|x86.ActiveCfg = Debug|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Debug|x86.Build.0 = Debug|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Release|Any CPU.Build.0 = Release|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Release|x64.ActiveCfg = Release|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Release|x64.Build.0 = Release|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Release|x86.ActiveCfg = Release|Any CPU + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {ACED222F-BDEB-48B6-BA6E-A28659080766} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {7A6A2996-1F41-4C94-A0A2-3AE963247243} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {3BA6251D-DE4E-4547-AAA9-25F4BA04C636} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {1A3AC28C-3AEE-40FE-B229-9E38BB609547} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {B68A0D0A-4785-48CB-864F-29E3A8ACA526} = {1CFF5568-8486-475F-81F6-06105C437528} + {A422C742-2CF9-409D-BDAE-15825AB62113} = {A566F2D7-F8FE-466A-8306-85F266B7E656} + {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5} = {1CFF5568-8486-475F-81F6-06105C437528} + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E} = {1CFF5568-8486-475F-81F6-06105C437528} + {F8B4100F-4014-4A1E-8130-D281453B79ED} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {12B940EF-A5D3-459D-BD36-A603834D1F7D} = {1CFF5568-8486-475F-81F6-06105C437528} + {B7AC87DF-9705-47D9-AC00-C230E577CA5D} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {3E1D77BD-70AF-4767-B00A-4A321D5AB2C3} = {1CFF5568-8486-475F-81F6-06105C437528} + {1ECB31E8-2EF0-41E2-8C71-CB9876D207F0} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {386A9769-59BF-4BE3-99D4-A9603E300729} = {1CFF5568-8486-475F-81F6-06105C437528} + {4F5020DB-9856-4A6F-B2CB-2C213FD749BC} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {B1A91FDB-A843-4CE5-A1AC-2ED48A158AA1} = {1CFF5568-8486-475F-81F6-06105C437528} + {A19E6CBD-8078-49F9-849E-2E484BFAF324} = {1CFF5568-8486-475F-81F6-06105C437528} + {93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7} = {A19E6CBD-8078-49F9-849E-2E484BFAF324} + {CDAE55EB-9438-4F54-B7ED-931D64324D5F} = {A19E6CBD-8078-49F9-849E-2E484BFAF324} + {AA532674-A61C-41E6-8F9A-ED53D79AF1EC} = {93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7} + {AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0} = {CDAE55EB-9438-4F54-B7ED-931D64324D5F} + {3C6162D7-0162-4BC2-BBF5-0554539A81CD} = {CDAE55EB-9438-4F54-B7ED-931D64324D5F} + {4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F} = {AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0} + {BA495B95-C463-4759-AA9D-34C6614B3511} = {AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0} + {3C9FA701-31FF-4747-B324-E0D252EAFD63} = {AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0} + {DEA5A48E-BC47-4E87-858C-282860CA196E} = {4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F} + {45D90299-D29F-4380-8FE8-98DF70508290} = {4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F} + {E0DEFF37-3706-4F61-98CA-904E2F964605} = {4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F} + {F858D526-7919-472B-8992-D627A4F3987C} = {BA495B95-C463-4759-AA9D-34C6614B3511} + {92669F13-F9C9-479F-ABA1-163D93ABCF95} = {BA495B95-C463-4759-AA9D-34C6614B3511} + {CC30B576-10ED-4DB2-A6C7-3A603E671523} = {BA495B95-C463-4759-AA9D-34C6614B3511} + {D22576BE-3D83-47C1-8B35-947C0B6DA8ED} = {3C9FA701-31FF-4747-B324-E0D252EAFD63} + {8DDAFE37-ED59-4710-9415-8EBA44CC6437} = {3C9FA701-31FF-4747-B324-E0D252EAFD63} + {8DDED681-AE8D-45EB-A22E-2FFB88620F9B} = {3C9FA701-31FF-4747-B324-E0D252EAFD63} + {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95} = {93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7} + {D303B458-9D84-4DDF-8781-2C0211672329} = {93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7} + {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} = {CDAE55EB-9438-4F54-B7ED-931D64324D5F} + {9AF99F6D-E8E7-443F-A965-D55B8E388836} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} + {ACA789EA-BD38-490B-A7F8-6A3A86985025} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} + {E71C48D2-AD56-4177-BBD7-6BB859A40C92} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} + {CC8CFF43-DC72-464C-A42D-55E023DE8500} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} + {A2AD98B1-2BED-4864-B573-77BE7B52FED2} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB} = {A566F2D7-F8FE-466A-8306-85F266B7E656} + {A566F2D7-F8FE-466A-8306-85F266B7E656} = {1CFF5568-8486-475F-81F6-06105C437528} + {7FC6DD65-0352-4139-8D08-B25C0A0403E3} = {4EAB66F9-C9CB-4E8A-BEE6-A14CD7FDE02F} + {61374D8E-F77C-4A31-AE07-35DAF1847369} = {1CFF5568-8486-475F-81F6-06105C437528} + {F4B8D5AF-D3CA-4910-A14D-E5BAEF0FD1DE} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {281F7EB5-ACE5-458F-BC88-46A8899DF3BA} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {8A22F22E-D10A-4897-A89A-DC76C267F6BB} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {5B0DDE6F-ED16-452F-90D3-F0B6086D51B3} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {FDBDB9F8-B3E2-4ACA-9FC6-E12FF3D95645} = {1CFF5568-8486-475F-81F6-06105C437528} + {25F0929B-2E04-4ED6-A0ED-5379A0A755B0} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {9E2B8160-3E76-4B33-86AB-DE35A5FCDB1E} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {B640DB80-C982-407B-A2EC-CD29AC77DDB8} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B} = {1CFF5568-8486-475F-81F6-06105C437528} + {D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5} = {1CFF5568-8486-475F-81F6-06105C437528} + {6B978BB7-6C6E-481A-BE21-2E9E93B06AA0} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5} + {B2ED85F6-FDBA-4AD7-8C13-1BF8BF464938} = {1CFF5568-8486-475F-81F6-06105C437528} + EndGlobalSection +EndGlobal diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/AWS.Lambda.Powertools.Metadata.csproj b/libraries/src/AWS.Lambda.Powertools.Metadata/AWS.Lambda.Powertools.Metadata.csproj new file mode 100644 index 00000000..fd59f9e9 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/AWS.Lambda.Powertools.Metadata.csproj @@ -0,0 +1,21 @@ + + + + + enable + enable + AWS.Lambda.Powertools.Metadata + Powertools for AWS Lambda (.NET) - Lambda Metadata package. Provides access to Lambda execution environment metadata from the Lambda Metadata Endpoint (LMDS). + AWS.Lambda.Powertools.Metadata + AWS.Lambda.Powertools.Metadata + true + + + + + + + + + + diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/Exceptions/LambdaMetadataException.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/Exceptions/LambdaMetadataException.cs new file mode 100644 index 00000000..39378bf6 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/Exceptions/LambdaMetadataException.cs @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +namespace AWS.Lambda.Powertools.Metadata.Exceptions; + +/// +/// Exception thrown when the Lambda Metadata Endpoint is unavailable or returns an error. +/// +/// This exception may be thrown when: +/// +/// The metadata endpoint environment variables are not set +/// The metadata endpoint returns a non-200 status code +/// Network errors occur when connecting to the endpoint +/// The response cannot be parsed +/// +/// +/// +public class LambdaMetadataException : Exception +{ + /// + /// Gets the HTTP status code from the metadata endpoint. + /// Returns -1 if not applicable. + /// + public int StatusCode { get; } + + /// + /// Constructs a new exception with the specified message. + /// + /// The error message. + public LambdaMetadataException(string message) : base(message) + { + StatusCode = -1; + } + + /// + /// Constructs a new exception with the specified message and cause. + /// + /// The error message. + /// The underlying cause. + public LambdaMetadataException(string message, Exception innerException) : base(message, innerException) + { + StatusCode = -1; + } + + /// + /// Constructs a new exception with the specified message and HTTP status code. + /// + /// The error message. + /// The HTTP status code from the metadata endpoint. + public LambdaMetadataException(string message, int statusCode) : base(message) + { + StatusCode = statusCode; + } +} diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/ILambdaMetadataHttpClient.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/ILambdaMetadataHttpClient.cs new file mode 100644 index 00000000..f0c4a51e --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/ILambdaMetadataHttpClient.cs @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +namespace AWS.Lambda.Powertools.Metadata.Internal; + +/// +/// Interface for the Lambda Metadata HTTP client. +/// +internal interface ILambdaMetadataHttpClient +{ + /// + /// Fetches metadata from the Lambda Metadata Endpoint synchronously. + /// + /// The Lambda metadata. + LambdaMetadata FetchMetadata(); + + /// + /// Fetches metadata from the Lambda Metadata Endpoint asynchronously. + /// + /// Cancellation token. + /// The Lambda metadata. + Task FetchMetadataAsync(CancellationToken cancellationToken = default); +} diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataHttpClient.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataHttpClient.cs new file mode 100644 index 00000000..343b8762 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataHttpClient.cs @@ -0,0 +1,186 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Net; +using System.Text.Json; +using AWS.Lambda.Powertools.Metadata.Exceptions; + +namespace AWS.Lambda.Powertools.Metadata.Internal; + +/// +/// Internal HTTP client for fetching metadata from the Lambda Metadata Endpoint. +/// +/// Uses for HTTP requests. The client is designed to be +/// AOT-compatible and uses source-generated JSON serialization. +/// +/// +internal class LambdaMetadataHttpClient : ILambdaMetadataHttpClient +{ + internal const string EnvMetadataApi = "AWS_LAMBDA_METADATA_API"; + internal const string EnvMetadataToken = "AWS_LAMBDA_METADATA_TOKEN"; + private const string ApiVersion = "2026-01-15"; + private const string MetadataPath = "/metadata/execution-environment"; + private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(1); + + private readonly HttpClient _httpClient; + + /// + /// Creates a new instance of the HTTP client. + /// + public LambdaMetadataHttpClient() : this(CreateDefaultHttpClient()) + { + } + + /// + /// Creates a new instance with a custom HttpClient (for testing). + /// + internal LambdaMetadataHttpClient(HttpClient httpClient) + { + _httpClient = httpClient; + } + + private static HttpClient CreateDefaultHttpClient() + { + var handler = new HttpClientHandler + { + AutomaticDecompression = DecompressionMethods.None + }; + + return new HttpClient(handler) + { + Timeout = Timeout + }; + } + + /// + public LambdaMetadata FetchMetadata() + { + var (token, api, url) = ValidateAndBuildUrl(); + + try + { + using var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add("Authorization", $"Bearer {token}"); + + using var response = _httpClient.Send(request); + return ProcessResponse(response); + } + catch (LambdaMetadataException) + { + throw; + } + catch (Exception ex) + { + throw new LambdaMetadataException($"Failed to fetch Lambda metadata: {ex.Message}", ex); + } + } + + /// + public async Task FetchMetadataAsync(CancellationToken cancellationToken = default) + { + var (token, api, url) = ValidateAndBuildUrl(); + + try + { + using var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add("Authorization", $"Bearer {token}"); + + using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); + return await ProcessResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + catch (LambdaMetadataException) + { + throw; + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception ex) + { + throw new LambdaMetadataException($"Failed to fetch Lambda metadata: {ex.Message}", ex); + } + } + + private (string token, string api, string url) ValidateAndBuildUrl() + { + var token = GetEnvironmentVariable(EnvMetadataToken); + var api = GetEnvironmentVariable(EnvMetadataApi); + + if (string.IsNullOrEmpty(token)) + { + throw new LambdaMetadataException( + $"Lambda metadata token not available. Ensure {EnvMetadataToken} is set."); + } + + if (string.IsNullOrEmpty(api)) + { + throw new LambdaMetadataException( + $"Lambda metadata API endpoint not available. Ensure {EnvMetadataApi} is set."); + } + + var url = $"http://{api}/{ApiVersion}{MetadataPath}"; + return (token, api, url); + } + + private LambdaMetadata ProcessResponse(HttpResponseMessage response) + { + var statusCode = (int)response.StatusCode; + + if (!response.IsSuccessStatusCode) + { + var errorContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + throw new LambdaMetadataException( + $"Metadata request failed with status {statusCode}: {errorContent}", + statusCode); + } + + var responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + return DeserializeMetadata(responseBody); + } + + private async Task ProcessResponseAsync(HttpResponseMessage response, CancellationToken cancellationToken) + { + var statusCode = (int)response.StatusCode; + + if (!response.IsSuccessStatusCode) + { + var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + throw new LambdaMetadataException( + $"Metadata request failed with status {statusCode}: {errorContent}", + statusCode); + } + + var responseBody = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return DeserializeMetadata(responseBody); + } + + private static LambdaMetadata DeserializeMetadata(string responseBody) + { + var metadata = JsonSerializer.Deserialize(responseBody, LambdaMetadataSerializerContext.Default.LambdaMetadata); + return metadata ?? throw new LambdaMetadataException("Failed to deserialize Lambda metadata response."); + } + + /// + /// Gets an environment variable value. + /// This method is virtual to allow overriding in tests. + /// + /// The environment variable name. + /// The value, or null if not set. + internal virtual string? GetEnvironmentVariable(string name) + { + return Environment.GetEnvironmentVariable(name); + } +} diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataSerializerContext.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataSerializerContext.cs new file mode 100644 index 00000000..09f2d6ae --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataSerializerContext.cs @@ -0,0 +1,30 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Text.Json.Serialization; + +namespace AWS.Lambda.Powertools.Metadata.Internal; + +/// +/// Source-generated JSON serializer context for AOT compatibility. +/// +[JsonSourceGenerationOptions( + PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + WriteIndented = false)] +[JsonSerializable(typeof(LambdaMetadata))] +internal partial class LambdaMetadataSerializerContext : JsonSerializerContext +{ +} diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/InternalsVisibleTo.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/InternalsVisibleTo.cs new file mode 100644 index 00000000..fc2655a6 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/InternalsVisibleTo.cs @@ -0,0 +1,20 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("AWS.Lambda.Powertools.Metadata.Tests")] +[assembly: InternalsVisibleTo("AWS.Lambda.Powertools.ConcurrencyTests")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadata.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadata.cs new file mode 100644 index 00000000..c2ebd6a3 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadata.cs @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Text.Json.Serialization; + +namespace AWS.Lambda.Powertools.Metadata; + +/// +/// Data class representing Lambda execution environment metadata. +/// +/// This class is immutable and contains metadata retrieved from the Lambda Metadata Endpoint (LMDS). +/// Use to obtain an instance. +/// +/// +/// Unknown properties in the JSON response are ignored to ensure forward compatibility. +/// +/// +/// +/// +/// var metadata = LambdaMetadataClient.Get(); +/// var azId = metadata.AvailabilityZoneId; +/// +/// +/// +public sealed class LambdaMetadata +{ + /// + /// Gets the Availability Zone ID. + /// + /// The Availability Zone ID is a unique identifier for the availability zone + /// where the Lambda function is executing (e.g., "use1-az1"). + /// + /// + [JsonPropertyName("AvailabilityZoneID")] + public string? AvailabilityZoneId { get; init; } + + /// + /// Default constructor for JSON deserialization. + /// + public LambdaMetadata() + { + } + + /// + /// Constructor with availability zone ID. + /// + /// The availability zone ID. + public LambdaMetadata(string? availabilityZoneId) + { + AvailabilityZoneId = availabilityZoneId; + } +} diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadataClient.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadataClient.cs new file mode 100644 index 00000000..a3800296 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadataClient.cs @@ -0,0 +1,243 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.Metadata.Exceptions; +using AWS.Lambda.Powertools.Metadata.Internal; + +namespace AWS.Lambda.Powertools.Metadata; + +/// +/// Client for accessing Lambda execution environment metadata. +/// +/// This utility provides idiomatic access to the Lambda Metadata Endpoint (LMDS), +/// eliminating boilerplate code for retrieving execution environment metadata +/// like Availability Zone ID. +/// +/// +/// Features: +/// +/// Automatic caching for the sandbox lifetime +/// Thread-safe access for concurrent executions +/// Async/await support +/// Lazy loading on first access +/// Native AOT compatible +/// +/// +/// +/// +/// Basic usage: +/// +/// public string HandleRequest(object input, ILambdaContext context) +/// { +/// var metadata = LambdaMetadataClient.Get(); +/// var azId = metadata.AvailabilityZoneId; +/// return $"{{\"az\": \"{azId}\"}}"; +/// } +/// +/// +/// +/// Async usage: +/// +/// public async Task<string> HandleRequestAsync(object input, ILambdaContext context) +/// { +/// var metadata = await LambdaMetadataClient.GetAsync(); +/// var azId = metadata.AvailabilityZoneId; +/// return $"{{\"az\": \"{azId}\"}}"; +/// } +/// +/// +/// +/// Eager loading during cold start: +/// +/// public class MyHandler +/// { +/// // Fetch during cold start +/// private static readonly LambdaMetadata Metadata = LambdaMetadataClient.Get(); +/// +/// public string HandleRequest(object input, ILambdaContext context) +/// { +/// return $"{{\"az\": \"{Metadata.AvailabilityZoneId}\"}}"; +/// } +/// } +/// +/// +/// +public static class LambdaMetadataClient +{ + private static readonly object Lock = new(); + private static readonly SemaphoreSlim AsyncLock = new(1, 1); + private static volatile LambdaMetadata? _cachedInstance; + private static ILambdaMetadataHttpClient _httpClient = new LambdaMetadataHttpClient(); + + /// + /// Retrieves the cached metadata, fetching from the endpoint if not cached. + /// + /// This method is thread-safe and handles concurrent access correctly. + /// The first call fetches metadata from the Lambda Metadata Endpoint, + /// subsequent calls return the cached value. + /// + /// + /// The instance. + /// + /// Thrown if the metadata endpoint is unavailable or returns an error. + /// + public static LambdaMetadata Get() + { + // Fast path: return cached instance if available (volatile read) + var instance = _cachedInstance; + if (instance is not null) + { + return instance; + } + + // Slow path: acquire lock and fetch + lock (Lock) + { + // Double-check after acquiring lock + instance = _cachedInstance; + if (instance is not null) + { + return instance; + } + + var newInstance = _httpClient.FetchMetadata(); + _cachedInstance = newInstance; + return newInstance; + } + } + + /// + /// Retrieves the cached metadata asynchronously, fetching from the endpoint if not cached. + /// + /// This method is thread-safe and handles concurrent access correctly. + /// The first call fetches metadata from the Lambda Metadata Endpoint, + /// subsequent calls return the cached value. + /// + /// + /// Cancellation token. + /// The instance. + /// + /// Thrown if the metadata endpoint is unavailable or returns an error. + /// + /// + /// Thrown if the operation is cancelled. + /// + public static async Task GetAsync(CancellationToken cancellationToken = default) + { + // Fast path: return cached instance if available (volatile read) + var instance = _cachedInstance; + if (instance is not null) + { + return instance; + } + + // Slow path: acquire async lock and fetch + await AsyncLock.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + // Double-check after acquiring lock + instance = _cachedInstance; + if (instance is not null) + { + return instance; + } + + var newInstance = await _httpClient.FetchMetadataAsync(cancellationToken).ConfigureAwait(false); + _cachedInstance = newInstance; + return newInstance; + } + finally + { + AsyncLock.Release(); + } + } + + /// + /// Forces a refresh of the cached metadata. + /// + /// This method clears the cache and fetches fresh metadata from the endpoint. + /// Use this only for advanced use cases where you need to force a refresh. + /// + /// + /// The refreshed instance. + /// + /// Thrown if the metadata endpoint is unavailable or returns an error. + /// + public static LambdaMetadata Refresh() + { + lock (Lock) + { + _cachedInstance = null; + var newInstance = _httpClient.FetchMetadata(); + _cachedInstance = newInstance; + return newInstance; + } + } + + /// + /// Forces a refresh of the cached metadata asynchronously. + /// + /// This method clears the cache and fetches fresh metadata from the endpoint. + /// Use this only for advanced use cases where you need to force a refresh. + /// + /// + /// Cancellation token. + /// The refreshed instance. + /// + /// Thrown if the metadata endpoint is unavailable or returns an error. + /// + /// + /// Thrown if the operation is cancelled. + /// + public static async Task RefreshAsync(CancellationToken cancellationToken = default) + { + await AsyncLock.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + _cachedInstance = null; + var newInstance = await _httpClient.FetchMetadataAsync(cancellationToken).ConfigureAwait(false); + _cachedInstance = newInstance; + return newInstance; + } + finally + { + AsyncLock.Release(); + } + } + + /// + /// Sets the HTTP client (for testing purposes only). + /// + /// The client to use. + internal static void SetHttpClient(ILambdaMetadataHttpClient client) + { + lock (Lock) + { + _httpClient = client; + _cachedInstance = null; + } + } + + /// + /// Resets the cached instance (for testing purposes only). + /// + internal static void ResetCache() + { + lock (Lock) + { + _cachedInstance = null; + } + } +} diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/README.md b/libraries/src/AWS.Lambda.Powertools.Metadata/README.md new file mode 100644 index 00000000..af163f86 --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/README.md @@ -0,0 +1,92 @@ +# AWS.Lambda.Powertools.Metadata + +Powertools for AWS Lambda (.NET) - Lambda Metadata package. + +This utility provides idiomatic access to the Lambda Metadata Endpoint (LMDS), eliminating boilerplate code for retrieving execution environment metadata like Availability Zone ID. + +## Features + +- **Automatic caching** for the sandbox lifetime +- **Thread-safe** access for concurrent executions +- **Lazy loading** on first access +- **Native AOT** compatible + +## Installation + +```bash +dotnet add package AWS.Lambda.Powertools.Metadata +``` + +## Usage + +### Basic Usage + +```csharp +using AWS.Lambda.Powertools.Metadata; + +public class Function +{ + public string FunctionHandler(object input, ILambdaContext context) + { + var metadata = LambdaMetadataClient.Get(); + var azId = metadata.AvailabilityZoneId; + + return $"Running in AZ: {azId}"; + } +} +``` + +### Eager Loading (Recommended for Production) + +```csharp +using AWS.Lambda.Powertools.Metadata; + +public class Function +{ + // Fetch during cold start for optimal performance + private static readonly LambdaMetadata Metadata = LambdaMetadataClient.Get(); + + public string FunctionHandler(object input, ILambdaContext context) + { + return $"Running in AZ: {Metadata.AvailabilityZoneId}"; + } +} +``` + +## Available Metadata + +| Property | Description | Example | +|----------|-------------|---------| +| `AvailabilityZoneId` | The Availability Zone ID where the function is executing | `use1-az1` | + +## Environment Variables + +The utility reads the following environment variables (automatically set by Lambda): + +| Variable | Description | +|----------|-------------| +| `AWS_LAMBDA_METADATA_API` | The metadata API endpoint | +| `AWS_LAMBDA_METADATA_TOKEN` | The authentication token for the metadata API | + +## Error Handling + +```csharp +try +{ + var metadata = LambdaMetadataClient.Get(); +} +catch (LambdaMetadataException ex) +{ + // Handle metadata fetch failure + Console.WriteLine($"Failed to get metadata: {ex.Message}"); + + if (ex.StatusCode > 0) + { + Console.WriteLine($"HTTP Status: {ex.StatusCode}"); + } +} +``` + +## License + +This library is licensed under the Apache License 2.0. diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/AWS.Lambda.Powertools.Metadata.Tests.csproj b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/AWS.Lambda.Powertools.Metadata.Tests.csproj new file mode 100644 index 00000000..af6353a8 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/AWS.Lambda.Powertools.Metadata.Tests.csproj @@ -0,0 +1,31 @@ + + + + enable + enable + AWS.Lambda.Powertools.Metadata.Tests + AWS.Lambda.Powertools.Metadata.Tests + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Exceptions/LambdaMetadataExceptionTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Exceptions/LambdaMetadataExceptionTests.cs new file mode 100644 index 00000000..c49b3ac8 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Exceptions/LambdaMetadataExceptionTests.cs @@ -0,0 +1,62 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.Metadata.Exceptions; +using FluentAssertions; +using Xunit; + +namespace AWS.Lambda.Powertools.Metadata.Tests.Exceptions; + +public class LambdaMetadataExceptionTests +{ + [Fact] + public void Constructor_WithMessage_Should_SetMessage() + { + // When + var exception = new LambdaMetadataException("Test message"); + + // Then + exception.Message.Should().Be("Test message"); + exception.StatusCode.Should().Be(-1); + exception.InnerException.Should().BeNull(); + } + + [Fact] + public void Constructor_WithMessageAndCause_Should_SetBoth() + { + // Given + var cause = new InvalidOperationException("Root cause"); + + // When + var exception = new LambdaMetadataException("Test message", cause); + + // Then + exception.Message.Should().Be("Test message"); + exception.InnerException.Should().BeSameAs(cause); + exception.StatusCode.Should().Be(-1); + } + + [Fact] + public void Constructor_WithMessageAndStatusCode_Should_SetBoth() + { + // When + var exception = new LambdaMetadataException("Test message", 500); + + // Then + exception.Message.Should().Be("Test message"); + exception.StatusCode.Should().Be(500); + exception.InnerException.Should().BeNull(); + } +} diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/LambdaMetadataHttpClientTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/LambdaMetadataHttpClientTests.cs new file mode 100644 index 00000000..85fae885 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/LambdaMetadataHttpClientTests.cs @@ -0,0 +1,189 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.Metadata.Exceptions; +using AWS.Lambda.Powertools.Metadata.Internal; +using FluentAssertions; +using Xunit; + +namespace AWS.Lambda.Powertools.Metadata.Tests.Internal; + +public class LambdaMetadataHttpClientTests +{ + private const string TestToken = "test-token-12345"; + + #region Synchronous FetchMetadata Tests + + [Fact] + public void FetchMetadata_Should_ThrowOnMissingToken() + { + // Given + var client = new TestableHttpClient(null, "localhost:8080"); + + // When/Then + var act = () => client.FetchMetadata(); + act.Should().Throw() + .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataToken}*"); + } + + [Fact] + public void FetchMetadata_Should_ThrowOnMissingApi() + { + // Given + var client = new TestableHttpClient(TestToken, null); + + // When/Then + var act = () => client.FetchMetadata(); + act.Should().Throw() + .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataApi}*"); + } + + [Fact] + public void FetchMetadata_Should_ThrowOnEmptyToken() + { + // Given + var client = new TestableHttpClient("", "localhost:8080"); + + // When/Then + var act = () => client.FetchMetadata(); + act.Should().Throw() + .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataToken}*"); + } + + [Fact] + public void FetchMetadata_Should_ThrowOnEmptyApi() + { + // Given + var client = new TestableHttpClient(TestToken, ""); + + // When/Then + var act = () => client.FetchMetadata(); + act.Should().Throw() + .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataApi}*"); + } + + #endregion + + #region Asynchronous FetchMetadataAsync Tests + + [Fact] + public async Task FetchMetadataAsync_Should_ThrowOnMissingToken() + { + // Given + var client = new TestableHttpClient(null, "localhost:8080"); + + // When/Then + var act = async () => await client.FetchMetadataAsync(); + await act.Should().ThrowAsync() + .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataToken}*"); + } + + [Fact] + public async Task FetchMetadataAsync_Should_ThrowOnMissingApi() + { + // Given + var client = new TestableHttpClient(TestToken, null); + + // When/Then + var act = async () => await client.FetchMetadataAsync(); + await act.Should().ThrowAsync() + .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataApi}*"); + } + + [Fact] + public async Task FetchMetadataAsync_Should_ThrowOnEmptyToken() + { + // Given + var client = new TestableHttpClient("", "localhost:8080"); + + // When/Then + var act = async () => await client.FetchMetadataAsync(); + await act.Should().ThrowAsync() + .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataToken}*"); + } + + [Fact] + public async Task FetchMetadataAsync_Should_ThrowOnEmptyApi() + { + // Given + var client = new TestableHttpClient(TestToken, ""); + + // When/Then + var act = async () => await client.FetchMetadataAsync(); + await act.Should().ThrowAsync() + .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataApi}*"); + } + + [Fact] + public async Task FetchMetadataAsync_Should_SupportCancellation() + { + // Given + var cts = new CancellationTokenSource(); + cts.Cancel(); + var client = new TestableHttpClient(TestToken, "localhost:8080"); + + // When/Then + var act = async () => await client.FetchMetadataAsync(cts.Token); + await act.Should().ThrowAsync(); + } + + #endregion + + #region Environment Variable Tests + + [Fact] + public void GetEnvironmentVariable_Should_ReturnCorrectValues() + { + // Given + var client = new TestableHttpClient(TestToken, "localhost:9000"); + + // When/Then + client.TestGetEnvironmentVariable(LambdaMetadataHttpClient.EnvMetadataToken) + .Should().Be(TestToken); + client.TestGetEnvironmentVariable(LambdaMetadataHttpClient.EnvMetadataApi) + .Should().Be("localhost:9000"); + client.TestGetEnvironmentVariable("UNKNOWN_VAR") + .Should().BeNull(); + } + + #endregion + + /// + /// Testable HTTP client that allows overriding environment variables. + /// + private class TestableHttpClient : LambdaMetadataHttpClient + { + private readonly string? _token; + private readonly string? _api; + + public TestableHttpClient(string? token, string? api) + { + _token = token; + _api = api; + } + + internal override string? GetEnvironmentVariable(string name) + { + return name switch + { + EnvMetadataToken => _token, + EnvMetadataApi => _api, + _ => null + }; + } + + public string? TestGetEnvironmentVariable(string name) => GetEnvironmentVariable(name); + } +} diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientCollection.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientCollection.cs new file mode 100644 index 00000000..4b563f05 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientCollection.cs @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using Xunit; + +namespace AWS.Lambda.Powertools.Metadata.Tests; + +/// +/// Collection definition to ensure tests that use the static LambdaMetadataClient +/// run sequentially and don't interfere with each other. +/// +[CollectionDefinition("LambdaMetadataClient", DisableParallelization = true)] +public class LambdaMetadataClientCollection +{ +} diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientConcurrencyTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientConcurrencyTests.cs new file mode 100644 index 00000000..7163810c --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientConcurrencyTests.cs @@ -0,0 +1,227 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.Metadata.Internal; +using FluentAssertions; +using NSubstitute; +using Xunit; + +namespace AWS.Lambda.Powertools.Metadata.Tests; + +[Collection("LambdaMetadataClient")] +public class LambdaMetadataClientConcurrencyTests : IDisposable +{ + private readonly ILambdaMetadataHttpClient _mockHttpClient; + + public LambdaMetadataClientConcurrencyTests() + { + _mockHttpClient = Substitute.For(); + LambdaMetadataClient.SetHttpClient(_mockHttpClient); + } + + public void Dispose() + { + LambdaMetadataClient.ResetCache(); + } + + [Fact] + public async Task Get_Should_BeThreadSafe() + { + // Given + var metadata = new LambdaMetadata("use1-az1"); + _mockHttpClient.FetchMetadata().Returns(metadata); + + const int threadCount = 50; + var startSignal = new TaskCompletionSource(); + var tasks = new List>(); + + // When - all threads try to get metadata simultaneously + for (var i = 0; i < threadCount; i++) + { + tasks.Add(Task.Run(async () => + { + await startSignal.Task; + return LambdaMetadataClient.Get(); + })); + } + + startSignal.SetResult(true); + var results = await Task.WhenAll(tasks); + + // Then - all threads should get the same instance + var firstResult = results[0]; + foreach (var result in results) + { + result.Should().BeSameAs(firstResult); + result.AvailabilityZoneId.Should().Be("use1-az1"); + } + + // Should only fetch once despite concurrent access + _mockHttpClient.Received(1).FetchMetadata(); + } + + [Fact] + public async Task GetAsync_Should_BeThreadSafe() + { + // Given + var metadata = new LambdaMetadata("use1-az2"); + _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata); + + const int threadCount = 50; + var startSignal = new TaskCompletionSource(); + var tasks = new List>(); + + // When - all tasks try to get metadata simultaneously + for (var i = 0; i < threadCount; i++) + { + tasks.Add(Task.Run(async () => + { + await startSignal.Task; + return await LambdaMetadataClient.GetAsync(); + })); + } + + startSignal.SetResult(true); + var results = await Task.WhenAll(tasks); + + // Then - all tasks should get the same instance + var firstResult = results[0]; + foreach (var result in results) + { + result.Should().BeSameAs(firstResult); + result.AvailabilityZoneId.Should().Be("use1-az2"); + } + + // Should only fetch once despite concurrent access + await _mockHttpClient.Received(1).FetchMetadataAsync(Arg.Any()); + } + + [Fact] + public async Task MixedSyncAndAsync_Should_BeThreadSafe() + { + // Given + var metadata = new LambdaMetadata("use1-az3"); + _mockHttpClient.FetchMetadata().Returns(metadata); + _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata); + + const int threadCount = 25; + var startSignal = new TaskCompletionSource(); + var syncTasks = new List>(); + var asyncTasks = new List>(); + + // When - mix of sync and async calls + for (var i = 0; i < threadCount; i++) + { + syncTasks.Add(Task.Run(async () => + { + await startSignal.Task; + return LambdaMetadataClient.Get(); + })); + + asyncTasks.Add(Task.Run(async () => + { + await startSignal.Task; + return await LambdaMetadataClient.GetAsync(); + })); + } + + startSignal.SetResult(true); + + var syncResults = await Task.WhenAll(syncTasks); + var asyncResults = await Task.WhenAll(asyncTasks); + + // Then - all should get the same instance + var allResults = syncResults.Concat(asyncResults).ToList(); + var firstResult = allResults[0]; + + foreach (var result in allResults) + { + result.Should().BeSameAs(firstResult); + result.AvailabilityZoneId.Should().Be("use1-az3"); + } + } + + [Fact] + public async Task ConcurrentRefresh_Should_BeThreadSafe() + { + // Given + var callCount = 0; + _mockHttpClient.FetchMetadata().Returns(_ => + { + var count = Interlocked.Increment(ref callCount); + return new LambdaMetadata($"use1-az{count}"); + }); + + const int threadCount = 10; + var startSignal = new TaskCompletionSource(); + var tasks = new List>(); + + // When - concurrent refresh calls + for (var i = 0; i < threadCount; i++) + { + tasks.Add(Task.Run(async () => + { + await startSignal.Task; + return LambdaMetadataClient.Refresh(); + })); + } + + startSignal.SetResult(true); + var results = await Task.WhenAll(tasks); + + // Then - all results should be valid (not null) + foreach (var result in results) + { + result.Should().NotBeNull(); + result.AvailabilityZoneId.Should().StartWith("use1-az"); + } + } + + [Fact] + public async Task ConcurrentRefreshAsync_Should_BeThreadSafe() + { + // Given + var callCount = 0; + _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(_ => + { + var count = Interlocked.Increment(ref callCount); + return Task.FromResult(new LambdaMetadata($"use1-az{count}")); + }); + + const int threadCount = 10; + var startSignal = new TaskCompletionSource(); + var tasks = new List>(); + + // When - concurrent async refresh calls + for (var i = 0; i < threadCount; i++) + { + tasks.Add(Task.Run(async () => + { + await startSignal.Task; + return await LambdaMetadataClient.RefreshAsync(); + })); + } + + startSignal.SetResult(true); + var results = await Task.WhenAll(tasks); + + // Then - all results should be valid (not null) + foreach (var result in results) + { + result.Should().NotBeNull(); + result.AvailabilityZoneId.Should().StartWith("use1-az"); + } + } +} diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientTests.cs new file mode 100644 index 00000000..34509bdb --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientTests.cs @@ -0,0 +1,315 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.Metadata.Exceptions; +using AWS.Lambda.Powertools.Metadata.Internal; +using FluentAssertions; +using NSubstitute; +using Xunit; + +namespace AWS.Lambda.Powertools.Metadata.Tests; + +[Collection("LambdaMetadataClient")] +public class LambdaMetadataClientTests : IDisposable +{ + private readonly ILambdaMetadataHttpClient _mockHttpClient; + + public LambdaMetadataClientTests() + { + _mockHttpClient = Substitute.For(); + LambdaMetadataClient.SetHttpClient(_mockHttpClient); + } + + public void Dispose() + { + LambdaMetadataClient.ResetCache(); + } + + #region Synchronous Get Tests + + [Fact] + public void Get_Should_ReturnMetadata() + { + // Given + var metadata = new LambdaMetadata("use1-az1"); + _mockHttpClient.FetchMetadata().Returns(metadata); + + // When + var result = LambdaMetadataClient.Get(); + + // Then + result.Should().NotBeNull(); + result.AvailabilityZoneId.Should().Be("use1-az1"); + } + + [Fact] + public void Get_Should_CacheMetadata() + { + // Given + var metadata = new LambdaMetadata("use1-az1"); + _mockHttpClient.FetchMetadata().Returns(metadata); + + // When + var first = LambdaMetadataClient.Get(); + var second = LambdaMetadataClient.Get(); + + // Then + first.Should().BeSameAs(second); + _mockHttpClient.Received(1).FetchMetadata(); + } + + [Fact] + public void Get_Should_ThrowExceptionOnError() + { + // Given + _mockHttpClient.FetchMetadata().Returns(_ => throw new LambdaMetadataException("Test error")); + + // When/Then + var act = () => LambdaMetadataClient.Get(); + act.Should().Throw() + .WithMessage("Test error"); + } + + [Fact] + public void Get_Should_ThrowExceptionWithStatusCode() + { + // Given + _mockHttpClient.FetchMetadata().Returns(_ => throw new LambdaMetadataException("Server error", 500)); + + // When/Then + var act = () => LambdaMetadataClient.Get(); + act.Should().Throw() + .Where(e => e.StatusCode == 500); + } + + #endregion + + #region Asynchronous GetAsync Tests + + [Fact] + public async Task GetAsync_Should_ReturnMetadata() + { + // Given + var metadata = new LambdaMetadata("use1-az1"); + _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata); + + // When + var result = await LambdaMetadataClient.GetAsync(); + + // Then + result.Should().NotBeNull(); + result.AvailabilityZoneId.Should().Be("use1-az1"); + } + + [Fact] + public async Task GetAsync_Should_CacheMetadata() + { + // Given + var metadata = new LambdaMetadata("use1-az1"); + _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata); + + // When + var first = await LambdaMetadataClient.GetAsync(); + var second = await LambdaMetadataClient.GetAsync(); + + // Then + first.Should().BeSameAs(second); + await _mockHttpClient.Received(1).FetchMetadataAsync(Arg.Any()); + } + + [Fact] + public async Task GetAsync_Should_ThrowExceptionOnError() + { + // Given + _mockHttpClient.FetchMetadataAsync(Arg.Any()) + .Returns(_ => throw new LambdaMetadataException("Test error")); + + // When/Then + var act = async () => await LambdaMetadataClient.GetAsync(); + await act.Should().ThrowAsync() + .WithMessage("Test error"); + } + + [Fact] + public async Task GetAsync_Should_SupportCancellation() + { + // Given + var cts = new CancellationTokenSource(); + cts.Cancel(); + + _mockHttpClient.FetchMetadataAsync(Arg.Any()) + .Returns(_ => throw new OperationCanceledException()); + + // When/Then + var act = async () => await LambdaMetadataClient.GetAsync(cts.Token); + await act.Should().ThrowAsync(); + } + + #endregion + + #region Synchronous Refresh Tests + + [Fact] + public void Refresh_Should_FetchNewMetadata() + { + // Given + var metadata1 = new LambdaMetadata("use1-az1"); + var metadata2 = new LambdaMetadata("use1-az2"); + _mockHttpClient.FetchMetadata().Returns(metadata1, metadata2); + + // When + var first = LambdaMetadataClient.Get(); + var refreshed = LambdaMetadataClient.Refresh(); + + // Then + first.AvailabilityZoneId.Should().Be("use1-az1"); + refreshed.AvailabilityZoneId.Should().Be("use1-az2"); + _mockHttpClient.Received(2).FetchMetadata(); + } + + [Fact] + public void Refresh_Should_UpdateCache() + { + // Given + var metadata1 = new LambdaMetadata("use1-az1"); + var metadata2 = new LambdaMetadata("use1-az2"); + _mockHttpClient.FetchMetadata().Returns(metadata1, metadata2); + + // When + LambdaMetadataClient.Get(); + var refreshed = LambdaMetadataClient.Refresh(); + var afterRefresh = LambdaMetadataClient.Get(); + + // Then + refreshed.Should().BeSameAs(afterRefresh); + refreshed.AvailabilityZoneId.Should().Be("use1-az2"); + } + + #endregion + + #region Asynchronous RefreshAsync Tests + + [Fact] + public async Task RefreshAsync_Should_FetchNewMetadata() + { + // Given + var metadata1 = new LambdaMetadata("use1-az1"); + var metadata2 = new LambdaMetadata("use1-az2"); + _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata1, metadata2); + + // When + var first = await LambdaMetadataClient.GetAsync(); + var refreshed = await LambdaMetadataClient.RefreshAsync(); + + // Then + first.AvailabilityZoneId.Should().Be("use1-az1"); + refreshed.AvailabilityZoneId.Should().Be("use1-az2"); + await _mockHttpClient.Received(2).FetchMetadataAsync(Arg.Any()); + } + + [Fact] + public async Task RefreshAsync_Should_SupportCancellation() + { + // Given + var cts = new CancellationTokenSource(); + cts.Cancel(); + + _mockHttpClient.FetchMetadataAsync(Arg.Any()) + .Returns(_ => throw new OperationCanceledException()); + + // When/Then + var act = async () => await LambdaMetadataClient.RefreshAsync(cts.Token); + await act.Should().ThrowAsync(); + } + + #endregion + + #region Cache Reset Tests + + [Fact] + public void ResetCache_Should_InvalidateCache() + { + // Given + var metadata1 = new LambdaMetadata("use1-az1"); + var metadata2 = new LambdaMetadata("use1-az2"); + _mockHttpClient.FetchMetadata().Returns(metadata1, metadata2); + + // When + var first = LambdaMetadataClient.Get(); + LambdaMetadataClient.ResetCache(); + var afterReset = LambdaMetadataClient.Get(); + + // Then + first.AvailabilityZoneId.Should().Be("use1-az1"); + afterReset.AvailabilityZoneId.Should().Be("use1-az2"); + _mockHttpClient.Received(2).FetchMetadata(); + } + + [Fact] + public void ResetCache_Should_AllowNewFetch() + { + // Given + var metadata = new LambdaMetadata("use1-az1"); + _mockHttpClient.FetchMetadata().Returns(metadata); + + // When + LambdaMetadataClient.Get(); + LambdaMetadataClient.ResetCache(); + LambdaMetadataClient.Get(); + + // Then + _mockHttpClient.Received(2).FetchMetadata(); + } + + #endregion + + #region Mixed Sync/Async Tests + + [Fact] + public async Task Get_And_GetAsync_Should_ShareCache() + { + // Given + var metadata = new LambdaMetadata("use1-az1"); + _mockHttpClient.FetchMetadata().Returns(metadata); + + // When - sync first + var syncResult = LambdaMetadataClient.Get(); + var asyncResult = await LambdaMetadataClient.GetAsync(); + + // Then + syncResult.Should().BeSameAs(asyncResult); + _mockHttpClient.Received(1).FetchMetadata(); + await _mockHttpClient.DidNotReceive().FetchMetadataAsync(Arg.Any()); + } + + [Fact] + public async Task GetAsync_And_Get_Should_ShareCache() + { + // Given + var metadata = new LambdaMetadata("use1-az1"); + _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata); + + // When - async first + var asyncResult = await LambdaMetadataClient.GetAsync(); + var syncResult = LambdaMetadataClient.Get(); + + // Then + asyncResult.Should().BeSameAs(syncResult); + await _mockHttpClient.Received(1).FetchMetadataAsync(Arg.Any()); + _mockHttpClient.DidNotReceive().FetchMetadata(); + } + + #endregion +} diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataTests.cs new file mode 100644 index 00000000..d3c4583b --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataTests.cs @@ -0,0 +1,72 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Text.Json; +using AWS.Lambda.Powertools.Metadata.Internal; +using FluentAssertions; +using Xunit; + +namespace AWS.Lambda.Powertools.Metadata.Tests; + +public class LambdaMetadataTests +{ + [Fact] + public void DefaultConstructor_Should_CreateInstanceWithNullValues() + { + // When + var metadata = new LambdaMetadata(); + + // Then + metadata.AvailabilityZoneId.Should().BeNull(); + } + + [Fact] + public void Constructor_WithAvailabilityZoneId_Should_SetValue() + { + // When + var metadata = new LambdaMetadata("use1-az1"); + + // Then + metadata.AvailabilityZoneId.Should().Be("use1-az1"); + } + + [Fact] + public void Deserialize_Should_MapJsonProperty() + { + // Given + var json = """{"AvailabilityZoneID": "euw1-az3"}"""; + + // When + var metadata = JsonSerializer.Deserialize(json, LambdaMetadataSerializerContext.Default.LambdaMetadata); + + // Then + metadata.Should().NotBeNull(); + metadata!.AvailabilityZoneId.Should().Be("euw1-az3"); + } + + [Fact] + public void Deserialize_Should_IgnoreUnknownFields() + { + // Given + var json = """{"AvailabilityZoneID": "apne1-az1", "UnknownField": "value", "AnotherField": 123}"""; + + // When + var metadata = JsonSerializer.Deserialize(json, LambdaMetadataSerializerContext.Default.LambdaMetadata); + + // Then + metadata.Should().NotBeNull(); + metadata!.AvailabilityZoneId.Should().Be("apne1-az1"); + } +} diff --git a/mkdocs.yml b/mkdocs.yml index c4df475c..0fa231fe 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,6 +28,7 @@ nav: - core/event_handler/appsync_events.md - core/event_handler/bedrock_agent_function.md - utilities/parameters.md + - utilities/metadata.md - utilities/jmespath-functions.md - utilities/kafka.md - Resources: @@ -132,6 +133,7 @@ plugins: - utilities/idempotency.md - utilities/batch-processing.md - utilities/parameters.md + - utilities/metadata.md - utilities/jmespath-functions.md - core/event_handler/appsync_events.md - core/event_handler/bedrock_agent_function.md From bd6a44f2d925de8e3d7dea5b3d49f8c9026781c7 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:48:52 +0000 Subject: [PATCH 2/6] refactor(metadata): simplify Lambda Metadata API and remove client pattern - Replace LambdaMetadataClient with static LambdaMetadata utility for simpler access - Remove async/await support in favor of synchronous static property access - Eliminate Refresh() and RefreshAsync() methods as metadata is immutable per sandbox - Rename IMetadataFetcher and consolidate internal HTTP client implementation - Remove LambdaMetadataClient and LambdaMetadataHttpClient classes - Update all tests to use new static property-based API - Simplify documentation to reflect streamlined usage patterns - Maintain automatic caching and thread-safe access for sandbox lifetime - Improves developer experience by reducing boilerplate code and API complexity --- docs/utilities/metadata.md | 231 +++---------- ...adataHttpClient.cs => IMetadataFetcher.cs} | 16 +- .../Internal/LambdaMetadataHttpClient.cs | 186 ----------- .../LambdaMetadataSerializerContext.cs | 2 +- .../Internal/MetadataFetcher.cs | 106 ++++++ .../LambdaMetadata.cs | 98 ++++-- .../LambdaMetadataClient.cs | 243 -------------- .../AWS.Lambda.Powertools.Metadata/README.md | 59 +--- .../Internal/LambdaMetadataHttpClientTests.cs | 189 ----------- .../Internal/MetadataFetcherTests.cs | 64 ++++ .../LambdaMetadataClientConcurrencyTests.cs | 227 ------------- .../LambdaMetadataClientTests.cs | 315 ------------------ ...lection.cs => LambdaMetadataCollection.cs} | 6 +- .../LambdaMetadataConcurrencyTests.cs | 70 ++++ .../LambdaMetadataTests.cs | 105 ++++-- 15 files changed, 450 insertions(+), 1467 deletions(-) rename libraries/src/AWS.Lambda.Powertools.Metadata/Internal/{ILambdaMetadataHttpClient.cs => IMetadataFetcher.cs} (54%) delete mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataHttpClient.cs create mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/Internal/MetadataFetcher.cs delete mode 100644 libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadataClient.cs delete mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/LambdaMetadataHttpClientTests.cs create mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/MetadataFetcherTests.cs delete mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientConcurrencyTests.cs delete mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientTests.cs rename libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/{LambdaMetadataClientCollection.cs => LambdaMetadataCollection.cs} (84%) create mode 100644 libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataConcurrencyTests.cs diff --git a/docs/utilities/metadata.md b/docs/utilities/metadata.md index 6907906f..2534c02c 100644 --- a/docs/utilities/metadata.md +++ b/docs/utilities/metadata.md @@ -4,264 +4,111 @@ description: Utility --- -The Lambda Metadata utility provides idiomatic access to the Lambda Metadata Endpoint (LMDS), eliminating boilerplate code for retrieving execution environment metadata like Availability Zone ID. +The Lambda Metadata utility provides access to the Lambda Metadata Endpoint (LMDS), giving you execution environment metadata like Availability Zone ID. ## Key features -* Retrieve Lambda execution environment metadata (e.g., Availability Zone ID) +* Retrieve Lambda execution environment metadata * Automatic caching for the sandbox lifetime -* Thread-safe access for concurrent executions -* Async/await support -* Lazy loading on first access +* Thread-safe access * Native AOT compatible -!!! warning "Migrating to v3" - - If you're upgrading to v3, please review the [Migration Guide v3](../migration-guide-v3.md) for important breaking changes including .NET 8 requirement and AWS SDK v4 migration. - ## Installation -Powertools for AWS Lambda (.NET) are available as NuGet packages. You can install the packages from [NuGet Gallery](https://www.nuget.org/packages?q=AWS+Lambda+Powertools*){target="_blank"} or from Visual Studio editor by searching `AWS.Lambda.Powertools*` to see various utilities available. - -* [AWS.Lambda.Powertools.Metadata](https://www.nuget.org/packages?q=AWS.Lambda.Powertools.Metadata): - - `dotnet add package AWS.Lambda.Powertools.Metadata` +```bash +dotnet add package AWS.Lambda.Powertools.Metadata +``` ## Getting started -The Lambda Metadata utility provides a simple static client to retrieve metadata about the Lambda execution environment. - -### Basic usage - -=== "Synchronous" - - ```c# hl_lines="10-11" - using AWS.Lambda.Powertools.Metadata; - - public class Function - { - public string FunctionHandler(object input, ILambdaContext context) - { - // Retrieve metadata (cached after first call) - var metadata = LambdaMetadataClient.Get(); - - // Access the Availability Zone ID - var azId = metadata.AvailabilityZoneId; - - return $"Running in AZ: {azId}"; - } - } - ``` - -=== "Asynchronous" - - ```c# hl_lines="10-11" - using AWS.Lambda.Powertools.Metadata; - - public class Function - { - public async Task FunctionHandler(object input, ILambdaContext context) - { - // Retrieve metadata asynchronously (cached after first call) - var metadata = await LambdaMetadataClient.GetAsync(); - - // Access the Availability Zone ID - var azId = metadata.AvailabilityZoneId; - - return $"Running in AZ: {azId}"; - } - } - ``` - -### Eager loading - -For optimal performance, you can fetch metadata during cold start by using a static field initializer: - -```c# hl_lines="7-8" +```csharp using AWS.Lambda.Powertools.Metadata; public class Function { - // Fetch during cold start - metadata is cached for the sandbox lifetime - private static readonly LambdaMetadata Metadata = LambdaMetadataClient.Get(); - - public string FunctionHandler(object input, ILambdaContext context) + public string Handler(object input, ILambdaContext context) { - // Use cached metadata - no additional API call - return $"Running in AZ: {Metadata.AvailabilityZoneId}"; + var azId = LambdaMetadata.AvailabilityZoneId; + return $"Running in AZ: {azId}"; } } ``` -This pattern ensures the metadata is fetched once during Lambda initialization and reused across all invocations. - ## Available metadata -The `LambdaMetadata` class provides the following properties: - -| Property | Type | Description | -|-----------------------|----------|-----------------------------------------------------------------------------| -| `AvailabilityZoneId` | `string` | The Availability Zone ID where the Lambda function is running (e.g., `use1-az1`) | - -## Refreshing metadata - -In most cases, you won't need to refresh metadata since it remains constant for the Lambda sandbox lifetime. However, if needed: - -=== "Synchronous" - - ```c# hl_lines="5" - using AWS.Lambda.Powertools.Metadata; - - // Force a refresh of cached metadata - var metadata = LambdaMetadataClient.Refresh(); - ``` - -=== "Asynchronous" - - ```c# hl_lines="5" - using AWS.Lambda.Powertools.Metadata; - - // Force a refresh of cached metadata asynchronously - var metadata = await LambdaMetadataClient.RefreshAsync(); - ``` +| Property | Type | Description | +|-----------------------|----------|----------------------------------------------------------| +| `AvailabilityZoneId` | `string` | The AZ where the function is running (e.g., `use1-az1`) | ## Error handling -The utility throws `LambdaMetadataException` when it cannot retrieve metadata: - -```c# hl_lines="9-13" +```csharp using AWS.Lambda.Powertools.Metadata; using AWS.Lambda.Powertools.Metadata.Exceptions; -public class Function +try { - public string FunctionHandler(object input, ILambdaContext context) - { - try - { - var metadata = LambdaMetadataClient.Get(); - return $"Running in AZ: {metadata.AvailabilityZoneId}"; - } - catch (LambdaMetadataException ex) - { - // Handle error - metadata endpoint unavailable - Console.WriteLine($"Failed to get metadata: {ex.Message}"); - - // Check HTTP status code if available - if (ex.StatusCode.HasValue) - { - Console.WriteLine($"HTTP Status: {ex.StatusCode}"); - } - - return "Unknown AZ"; - } - } + var azId = LambdaMetadata.AvailabilityZoneId; +} +catch (LambdaMetadataException ex) +{ + Console.WriteLine($"Failed to get metadata: {ex.Message}"); + + if (ex.StatusCode.HasValue) + Console.WriteLine($"HTTP Status: {ex.StatusCode}"); } ``` -### Common error scenarios - -| Scenario | Exception Message | -|-----------------------------------|-------------------------------------------------------------| -| Missing metadata token | `Lambda metadata token not available. Ensure AWS_LAMBDA_METADATA_TOKEN is set.` | -| Missing metadata API endpoint | `Lambda metadata API endpoint not available. Ensure AWS_LAMBDA_METADATA_API is set.` | -| HTTP error from metadata endpoint | `Metadata request failed with status {code}: {message}` | -| Deserialization failure | `Failed to deserialize Lambda metadata response.` | - -## Environment variables - -The utility uses the following environment variables (automatically set by the Lambda runtime): - -| Environment Variable | Description | -|-----------------------------|--------------------------------------------------| -| `AWS_LAMBDA_METADATA_API` | The metadata API endpoint | -| `AWS_LAMBDA_METADATA_TOKEN` | Authentication token for the metadata API | - -!!! note - These environment variables are automatically configured by the Lambda runtime. You don't need to set them manually. - -## Thread safety - -The `LambdaMetadataClient` is fully thread-safe: - -* Uses double-check locking pattern for synchronous access -* Uses `SemaphoreSlim` for asynchronous access -* Cached metadata is stored in a `volatile` field for safe concurrent reads -* Both sync and async methods share the same cache - -This means you can safely call `Get()` or `GetAsync()` from multiple concurrent Lambda invocations without race conditions. - -## Cancellation support +## Refreshing metadata -The async methods support cancellation tokens: +Metadata remains constant for the Lambda sandbox lifetime. If you need to force a refresh: -```c# hl_lines="5-6" -using AWS.Lambda.Powertools.Metadata; - -public async Task FunctionHandler(object input, ILambdaContext context) -{ - using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(500)); - var metadata = await LambdaMetadataClient.GetAsync(cts.Token); - return metadata.AvailabilityZoneId; -} +```csharp +LambdaMetadata.Refresh(); ``` -## AOT support - -This utility is fully compatible with Native AOT compilation. It uses source-generated JSON serialization to avoid reflection-based deserialization. +## Thread safety -No additional configuration is required for AOT support. +`LambdaMetadata.AvailabilityZoneId` is thread-safe. You can access it from multiple concurrent invocations without race conditions. ## Use cases -### Multi-AZ routing decisions +### Multi-AZ routing -```c# +```csharp using AWS.Lambda.Powertools.Metadata; public class Function { - private static readonly LambdaMetadata Metadata = LambdaMetadataClient.Get(); - - public async Task FunctionHandler(OrderRequest request, ILambdaContext context) + public async Task Handler(OrderRequest request, ILambdaContext context) { - // Route to AZ-local resources for lower latency - var azId = Metadata.AvailabilityZoneId; - var endpoint = GetAzLocalEndpoint(azId); - - return await ProcessOrder(request, endpoint); - } - - private string GetAzLocalEndpoint(string azId) - { - return azId switch + var endpoint = LambdaMetadata.AvailabilityZoneId switch { "use1-az1" => "https://service-az1.internal", "use1-az2" => "https://service-az2.internal", _ => "https://service.internal" }; + + return await ProcessOrder(request, endpoint); } } ``` -### Logging and observability +### Logging -```c# +```csharp using AWS.Lambda.Powertools.Logging; using AWS.Lambda.Powertools.Metadata; public class Function { - private static readonly LambdaMetadata Metadata = LambdaMetadataClient.Get(); - public Function() { - // Add AZ ID to all log entries - Logger.AppendKey("availability_zone_id", Metadata.AvailabilityZoneId); + Logger.AppendKey("az_id", LambdaMetadata.AvailabilityZoneId); } [Logging] - public string FunctionHandler(object input, ILambdaContext context) + public string Handler(object input, ILambdaContext context) { Logger.LogInformation("Processing request"); return "Success"; diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/ILambdaMetadataHttpClient.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/IMetadataFetcher.cs similarity index 54% rename from libraries/src/AWS.Lambda.Powertools.Metadata/Internal/ILambdaMetadataHttpClient.cs rename to libraries/src/AWS.Lambda.Powertools.Metadata/Internal/IMetadataFetcher.cs index f0c4a51e..fc437c48 100644 --- a/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/ILambdaMetadataHttpClient.cs +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/IMetadataFetcher.cs @@ -16,20 +16,12 @@ namespace AWS.Lambda.Powertools.Metadata.Internal; /// -/// Interface for the Lambda Metadata HTTP client. +/// Internal interface for fetching Lambda metadata. /// -internal interface ILambdaMetadataHttpClient +internal interface IMetadataFetcher { /// - /// Fetches metadata from the Lambda Metadata Endpoint synchronously. + /// Fetches metadata from the Lambda Metadata Endpoint. /// - /// The Lambda metadata. - LambdaMetadata FetchMetadata(); - - /// - /// Fetches metadata from the Lambda Metadata Endpoint asynchronously. - /// - /// Cancellation token. - /// The Lambda metadata. - Task FetchMetadataAsync(CancellationToken cancellationToken = default); + MetadataValues Fetch(); } diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataHttpClient.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataHttpClient.cs deleted file mode 100644 index 343b8762..00000000 --- a/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataHttpClient.cs +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using System.Net; -using System.Text.Json; -using AWS.Lambda.Powertools.Metadata.Exceptions; - -namespace AWS.Lambda.Powertools.Metadata.Internal; - -/// -/// Internal HTTP client for fetching metadata from the Lambda Metadata Endpoint. -/// -/// Uses for HTTP requests. The client is designed to be -/// AOT-compatible and uses source-generated JSON serialization. -/// -/// -internal class LambdaMetadataHttpClient : ILambdaMetadataHttpClient -{ - internal const string EnvMetadataApi = "AWS_LAMBDA_METADATA_API"; - internal const string EnvMetadataToken = "AWS_LAMBDA_METADATA_TOKEN"; - private const string ApiVersion = "2026-01-15"; - private const string MetadataPath = "/metadata/execution-environment"; - private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(1); - - private readonly HttpClient _httpClient; - - /// - /// Creates a new instance of the HTTP client. - /// - public LambdaMetadataHttpClient() : this(CreateDefaultHttpClient()) - { - } - - /// - /// Creates a new instance with a custom HttpClient (for testing). - /// - internal LambdaMetadataHttpClient(HttpClient httpClient) - { - _httpClient = httpClient; - } - - private static HttpClient CreateDefaultHttpClient() - { - var handler = new HttpClientHandler - { - AutomaticDecompression = DecompressionMethods.None - }; - - return new HttpClient(handler) - { - Timeout = Timeout - }; - } - - /// - public LambdaMetadata FetchMetadata() - { - var (token, api, url) = ValidateAndBuildUrl(); - - try - { - using var request = new HttpRequestMessage(HttpMethod.Get, url); - request.Headers.Add("Authorization", $"Bearer {token}"); - - using var response = _httpClient.Send(request); - return ProcessResponse(response); - } - catch (LambdaMetadataException) - { - throw; - } - catch (Exception ex) - { - throw new LambdaMetadataException($"Failed to fetch Lambda metadata: {ex.Message}", ex); - } - } - - /// - public async Task FetchMetadataAsync(CancellationToken cancellationToken = default) - { - var (token, api, url) = ValidateAndBuildUrl(); - - try - { - using var request = new HttpRequestMessage(HttpMethod.Get, url); - request.Headers.Add("Authorization", $"Bearer {token}"); - - using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); - return await ProcessResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - catch (LambdaMetadataException) - { - throw; - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - throw new LambdaMetadataException($"Failed to fetch Lambda metadata: {ex.Message}", ex); - } - } - - private (string token, string api, string url) ValidateAndBuildUrl() - { - var token = GetEnvironmentVariable(EnvMetadataToken); - var api = GetEnvironmentVariable(EnvMetadataApi); - - if (string.IsNullOrEmpty(token)) - { - throw new LambdaMetadataException( - $"Lambda metadata token not available. Ensure {EnvMetadataToken} is set."); - } - - if (string.IsNullOrEmpty(api)) - { - throw new LambdaMetadataException( - $"Lambda metadata API endpoint not available. Ensure {EnvMetadataApi} is set."); - } - - var url = $"http://{api}/{ApiVersion}{MetadataPath}"; - return (token, api, url); - } - - private LambdaMetadata ProcessResponse(HttpResponseMessage response) - { - var statusCode = (int)response.StatusCode; - - if (!response.IsSuccessStatusCode) - { - var errorContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - throw new LambdaMetadataException( - $"Metadata request failed with status {statusCode}: {errorContent}", - statusCode); - } - - var responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - return DeserializeMetadata(responseBody); - } - - private async Task ProcessResponseAsync(HttpResponseMessage response, CancellationToken cancellationToken) - { - var statusCode = (int)response.StatusCode; - - if (!response.IsSuccessStatusCode) - { - var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); - throw new LambdaMetadataException( - $"Metadata request failed with status {statusCode}: {errorContent}", - statusCode); - } - - var responseBody = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); - return DeserializeMetadata(responseBody); - } - - private static LambdaMetadata DeserializeMetadata(string responseBody) - { - var metadata = JsonSerializer.Deserialize(responseBody, LambdaMetadataSerializerContext.Default.LambdaMetadata); - return metadata ?? throw new LambdaMetadataException("Failed to deserialize Lambda metadata response."); - } - - /// - /// Gets an environment variable value. - /// This method is virtual to allow overriding in tests. - /// - /// The environment variable name. - /// The value, or null if not set. - internal virtual string? GetEnvironmentVariable(string name) - { - return Environment.GetEnvironmentVariable(name); - } -} diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataSerializerContext.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataSerializerContext.cs index 09f2d6ae..b90be24d 100644 --- a/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataSerializerContext.cs +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/LambdaMetadataSerializerContext.cs @@ -24,7 +24,7 @@ namespace AWS.Lambda.Powertools.Metadata.Internal; PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, WriteIndented = false)] -[JsonSerializable(typeof(LambdaMetadata))] +[JsonSerializable(typeof(MetadataValues))] internal partial class LambdaMetadataSerializerContext : JsonSerializerContext { } diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/MetadataFetcher.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/MetadataFetcher.cs new file mode 100644 index 00000000..c85dfeec --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/Internal/MetadataFetcher.cs @@ -0,0 +1,106 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System.Net; +using System.Text.Json; +using AWS.Lambda.Powertools.Metadata.Exceptions; + +namespace AWS.Lambda.Powertools.Metadata.Internal; + +/// +/// Fetches metadata from the Lambda Metadata Endpoint (LMDS). +/// +internal sealed class MetadataFetcher : IMetadataFetcher +{ + private const string EnvMetadataApi = "AWS_LAMBDA_METADATA_API"; + private const string EnvMetadataToken = "AWS_LAMBDA_METADATA_TOKEN"; + private const string ApiVersion = "2026-01-15"; + private const string MetadataPath = "/metadata/execution-environment"; + + private readonly HttpClient _httpClient; + + public MetadataFetcher() : this(CreateHttpClient()) + { + } + + internal MetadataFetcher(HttpClient httpClient) + { + _httpClient = httpClient; + } + + private static HttpClient CreateHttpClient() + { + return new HttpClient(new HttpClientHandler + { + AutomaticDecompression = DecompressionMethods.None + }) + { + Timeout = TimeSpan.FromSeconds(1) + }; + } + + public MetadataValues Fetch() + { + var (token, url) = GetEndpointInfo(); + + try + { + using var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Add("Authorization", $"Bearer {token}"); + + using var response = _httpClient.Send(request); + return ProcessResponse(response); + } + catch (LambdaMetadataException) + { + throw; + } + catch (Exception ex) + { + throw new LambdaMetadataException($"Failed to fetch Lambda metadata: {ex.Message}", ex); + } + } + + private static (string token, string url) GetEndpointInfo() + { + var token = Environment.GetEnvironmentVariable(EnvMetadataToken); + var api = Environment.GetEnvironmentVariable(EnvMetadataApi); + + if (string.IsNullOrEmpty(token)) + throw new LambdaMetadataException( + $"Lambda metadata token not available. Ensure {EnvMetadataToken} is set."); + + if (string.IsNullOrEmpty(api)) + throw new LambdaMetadataException( + $"Lambda metadata API endpoint not available. Ensure {EnvMetadataApi} is set."); + + return (token, $"http://{api}/{ApiVersion}{MetadataPath}"); + } + + private static MetadataValues ProcessResponse(HttpResponseMessage response) + { + if (!response.IsSuccessStatusCode) + { + var error = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + throw new LambdaMetadataException( + $"Metadata request failed with status {(int)response.StatusCode}: {error}", + (int)response.StatusCode); + } + + var body = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + return JsonSerializer.Deserialize(body, LambdaMetadataSerializerContext.Default.MetadataValues) + ?? throw new LambdaMetadataException("Failed to deserialize Lambda metadata response."); + } +} diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadata.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadata.cs index c2ebd6a3..192ea100 100644 --- a/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadata.cs +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadata.cs @@ -14,50 +14,108 @@ */ using System.Text.Json.Serialization; +using AWS.Lambda.Powertools.Metadata.Exceptions; +using AWS.Lambda.Powertools.Metadata.Internal; namespace AWS.Lambda.Powertools.Metadata; /// -/// Data class representing Lambda execution environment metadata. +/// Provides access to Lambda execution environment metadata from the Lambda Metadata Endpoint (LMDS). /// -/// This class is immutable and contains metadata retrieved from the Lambda Metadata Endpoint (LMDS). -/// Use to obtain an instance. -/// -/// -/// Unknown properties in the JSON response are ignored to ensure forward compatibility. +/// Metadata is automatically fetched on first access and cached for the Lambda sandbox lifetime. /// /// /// /// -/// var metadata = LambdaMetadataClient.Get(); -/// var azId = metadata.AvailabilityZoneId; +/// var azId = LambdaMetadata.AvailabilityZoneId; /// /// -/// -public sealed class LambdaMetadata +public static class LambdaMetadata { + private static readonly object Lock = new(); + private static volatile MetadataValues? _cached; + private static IMetadataFetcher _fetcher = new MetadataFetcher(); + /// - /// Gets the Availability Zone ID. + /// Gets the Availability Zone ID where the Lambda function is executing. + /// + /// Example value: "use1-az1" + /// + /// Thrown if the metadata endpoint is unavailable or returns an error. + /// + public static string? AvailabilityZoneId => GetCached().AvailabilityZoneId; + + /// + /// Forces a refresh of the cached metadata. /// - /// The Availability Zone ID is a unique identifier for the availability zone - /// where the Lambda function is executing (e.g., "use1-az1"). + /// In most cases, you don't need this since metadata remains constant for the + /// Lambda sandbox lifetime. /// /// - [JsonPropertyName("AvailabilityZoneID")] - public string? AvailabilityZoneId { get; init; } + /// + /// Thrown if the metadata endpoint is unavailable or returns an error. + /// + public static void Refresh() + { + lock (Lock) + { + _cached = _fetcher.Fetch(); + } + } + + private static MetadataValues GetCached() + { + var instance = _cached; + if (instance is not null) + return instance; + + lock (Lock) + { + instance = _cached; + if (instance is not null) + return instance; + + var newInstance = _fetcher.Fetch(); + _cached = newInstance; + return newInstance; + } + } /// - /// Default constructor for JSON deserialization. + /// Sets the metadata fetcher (for testing only). /// - public LambdaMetadata() + internal static void SetFetcher(IMetadataFetcher fetcher) { + lock (Lock) + { + _fetcher = fetcher; + _cached = null; + } } /// - /// Constructor with availability zone ID. + /// Resets the cached instance (for testing only). /// - /// The availability zone ID. - public LambdaMetadata(string? availabilityZoneId) + internal static void Reset() + { + lock (Lock) + { + _cached = null; + } + } +} + +/// +/// Internal class for JSON deserialization of metadata values. +/// +internal sealed class MetadataValues +{ + [JsonPropertyName("AvailabilityZoneID")] + public string? AvailabilityZoneId { get; init; } + + public MetadataValues() { } + + public MetadataValues(string? availabilityZoneId) { AvailabilityZoneId = availabilityZoneId; } diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadataClient.cs b/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadataClient.cs deleted file mode 100644 index a3800296..00000000 --- a/libraries/src/AWS.Lambda.Powertools.Metadata/LambdaMetadataClient.cs +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using AWS.Lambda.Powertools.Metadata.Exceptions; -using AWS.Lambda.Powertools.Metadata.Internal; - -namespace AWS.Lambda.Powertools.Metadata; - -/// -/// Client for accessing Lambda execution environment metadata. -/// -/// This utility provides idiomatic access to the Lambda Metadata Endpoint (LMDS), -/// eliminating boilerplate code for retrieving execution environment metadata -/// like Availability Zone ID. -/// -/// -/// Features: -/// -/// Automatic caching for the sandbox lifetime -/// Thread-safe access for concurrent executions -/// Async/await support -/// Lazy loading on first access -/// Native AOT compatible -/// -/// -/// -/// -/// Basic usage: -/// -/// public string HandleRequest(object input, ILambdaContext context) -/// { -/// var metadata = LambdaMetadataClient.Get(); -/// var azId = metadata.AvailabilityZoneId; -/// return $"{{\"az\": \"{azId}\"}}"; -/// } -/// -/// -/// -/// Async usage: -/// -/// public async Task<string> HandleRequestAsync(object input, ILambdaContext context) -/// { -/// var metadata = await LambdaMetadataClient.GetAsync(); -/// var azId = metadata.AvailabilityZoneId; -/// return $"{{\"az\": \"{azId}\"}}"; -/// } -/// -/// -/// -/// Eager loading during cold start: -/// -/// public class MyHandler -/// { -/// // Fetch during cold start -/// private static readonly LambdaMetadata Metadata = LambdaMetadataClient.Get(); -/// -/// public string HandleRequest(object input, ILambdaContext context) -/// { -/// return $"{{\"az\": \"{Metadata.AvailabilityZoneId}\"}}"; -/// } -/// } -/// -/// -/// -public static class LambdaMetadataClient -{ - private static readonly object Lock = new(); - private static readonly SemaphoreSlim AsyncLock = new(1, 1); - private static volatile LambdaMetadata? _cachedInstance; - private static ILambdaMetadataHttpClient _httpClient = new LambdaMetadataHttpClient(); - - /// - /// Retrieves the cached metadata, fetching from the endpoint if not cached. - /// - /// This method is thread-safe and handles concurrent access correctly. - /// The first call fetches metadata from the Lambda Metadata Endpoint, - /// subsequent calls return the cached value. - /// - /// - /// The instance. - /// - /// Thrown if the metadata endpoint is unavailable or returns an error. - /// - public static LambdaMetadata Get() - { - // Fast path: return cached instance if available (volatile read) - var instance = _cachedInstance; - if (instance is not null) - { - return instance; - } - - // Slow path: acquire lock and fetch - lock (Lock) - { - // Double-check after acquiring lock - instance = _cachedInstance; - if (instance is not null) - { - return instance; - } - - var newInstance = _httpClient.FetchMetadata(); - _cachedInstance = newInstance; - return newInstance; - } - } - - /// - /// Retrieves the cached metadata asynchronously, fetching from the endpoint if not cached. - /// - /// This method is thread-safe and handles concurrent access correctly. - /// The first call fetches metadata from the Lambda Metadata Endpoint, - /// subsequent calls return the cached value. - /// - /// - /// Cancellation token. - /// The instance. - /// - /// Thrown if the metadata endpoint is unavailable or returns an error. - /// - /// - /// Thrown if the operation is cancelled. - /// - public static async Task GetAsync(CancellationToken cancellationToken = default) - { - // Fast path: return cached instance if available (volatile read) - var instance = _cachedInstance; - if (instance is not null) - { - return instance; - } - - // Slow path: acquire async lock and fetch - await AsyncLock.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - // Double-check after acquiring lock - instance = _cachedInstance; - if (instance is not null) - { - return instance; - } - - var newInstance = await _httpClient.FetchMetadataAsync(cancellationToken).ConfigureAwait(false); - _cachedInstance = newInstance; - return newInstance; - } - finally - { - AsyncLock.Release(); - } - } - - /// - /// Forces a refresh of the cached metadata. - /// - /// This method clears the cache and fetches fresh metadata from the endpoint. - /// Use this only for advanced use cases where you need to force a refresh. - /// - /// - /// The refreshed instance. - /// - /// Thrown if the metadata endpoint is unavailable or returns an error. - /// - public static LambdaMetadata Refresh() - { - lock (Lock) - { - _cachedInstance = null; - var newInstance = _httpClient.FetchMetadata(); - _cachedInstance = newInstance; - return newInstance; - } - } - - /// - /// Forces a refresh of the cached metadata asynchronously. - /// - /// This method clears the cache and fetches fresh metadata from the endpoint. - /// Use this only for advanced use cases where you need to force a refresh. - /// - /// - /// Cancellation token. - /// The refreshed instance. - /// - /// Thrown if the metadata endpoint is unavailable or returns an error. - /// - /// - /// Thrown if the operation is cancelled. - /// - public static async Task RefreshAsync(CancellationToken cancellationToken = default) - { - await AsyncLock.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - _cachedInstance = null; - var newInstance = await _httpClient.FetchMetadataAsync(cancellationToken).ConfigureAwait(false); - _cachedInstance = newInstance; - return newInstance; - } - finally - { - AsyncLock.Release(); - } - } - - /// - /// Sets the HTTP client (for testing purposes only). - /// - /// The client to use. - internal static void SetHttpClient(ILambdaMetadataHttpClient client) - { - lock (Lock) - { - _httpClient = client; - _cachedInstance = null; - } - } - - /// - /// Resets the cached instance (for testing purposes only). - /// - internal static void ResetCache() - { - lock (Lock) - { - _cachedInstance = null; - } - } -} diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/README.md b/libraries/src/AWS.Lambda.Powertools.Metadata/README.md index af163f86..3881a9c1 100644 --- a/libraries/src/AWS.Lambda.Powertools.Metadata/README.md +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/README.md @@ -1,15 +1,8 @@ # AWS.Lambda.Powertools.Metadata -Powertools for AWS Lambda (.NET) - Lambda Metadata package. +Powertools for AWS Lambda (.NET) - Lambda Metadata utility. -This utility provides idiomatic access to the Lambda Metadata Endpoint (LMDS), eliminating boilerplate code for retrieving execution environment metadata like Availability Zone ID. - -## Features - -- **Automatic caching** for the sandbox lifetime -- **Thread-safe** access for concurrent executions -- **Lazy loading** on first access -- **Native AOT** compatible +Provides access to Lambda execution environment metadata from the Lambda Metadata Endpoint (LMDS). ## Installation @@ -19,74 +12,38 @@ dotnet add package AWS.Lambda.Powertools.Metadata ## Usage -### Basic Usage - ```csharp using AWS.Lambda.Powertools.Metadata; public class Function { - public string FunctionHandler(object input, ILambdaContext context) + public string Handler(object input, ILambdaContext context) { - var metadata = LambdaMetadataClient.Get(); - var azId = metadata.AvailabilityZoneId; - + var azId = LambdaMetadata.AvailabilityZoneId; return $"Running in AZ: {azId}"; } } ``` -### Eager Loading (Recommended for Production) - -```csharp -using AWS.Lambda.Powertools.Metadata; - -public class Function -{ - // Fetch during cold start for optimal performance - private static readonly LambdaMetadata Metadata = LambdaMetadataClient.Get(); - - public string FunctionHandler(object input, ILambdaContext context) - { - return $"Running in AZ: {Metadata.AvailabilityZoneId}"; - } -} -``` - ## Available Metadata | Property | Description | Example | |----------|-------------|---------| -| `AvailabilityZoneId` | The Availability Zone ID where the function is executing | `use1-az1` | - -## Environment Variables - -The utility reads the following environment variables (automatically set by Lambda): - -| Variable | Description | -|----------|-------------| -| `AWS_LAMBDA_METADATA_API` | The metadata API endpoint | -| `AWS_LAMBDA_METADATA_TOKEN` | The authentication token for the metadata API | +| `AvailabilityZoneId` | The AZ where the function is executing | `use1-az1` | ## Error Handling ```csharp try { - var metadata = LambdaMetadataClient.Get(); + var azId = LambdaMetadata.AvailabilityZoneId; } catch (LambdaMetadataException ex) { - // Handle metadata fetch failure - Console.WriteLine($"Failed to get metadata: {ex.Message}"); - - if (ex.StatusCode > 0) - { - Console.WriteLine($"HTTP Status: {ex.StatusCode}"); - } + Console.WriteLine($"Failed: {ex.Message}"); } ``` ## License -This library is licensed under the Apache License 2.0. +Apache License 2.0 diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/LambdaMetadataHttpClientTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/LambdaMetadataHttpClientTests.cs deleted file mode 100644 index 85fae885..00000000 --- a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/LambdaMetadataHttpClientTests.cs +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using AWS.Lambda.Powertools.Metadata.Exceptions; -using AWS.Lambda.Powertools.Metadata.Internal; -using FluentAssertions; -using Xunit; - -namespace AWS.Lambda.Powertools.Metadata.Tests.Internal; - -public class LambdaMetadataHttpClientTests -{ - private const string TestToken = "test-token-12345"; - - #region Synchronous FetchMetadata Tests - - [Fact] - public void FetchMetadata_Should_ThrowOnMissingToken() - { - // Given - var client = new TestableHttpClient(null, "localhost:8080"); - - // When/Then - var act = () => client.FetchMetadata(); - act.Should().Throw() - .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataToken}*"); - } - - [Fact] - public void FetchMetadata_Should_ThrowOnMissingApi() - { - // Given - var client = new TestableHttpClient(TestToken, null); - - // When/Then - var act = () => client.FetchMetadata(); - act.Should().Throw() - .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataApi}*"); - } - - [Fact] - public void FetchMetadata_Should_ThrowOnEmptyToken() - { - // Given - var client = new TestableHttpClient("", "localhost:8080"); - - // When/Then - var act = () => client.FetchMetadata(); - act.Should().Throw() - .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataToken}*"); - } - - [Fact] - public void FetchMetadata_Should_ThrowOnEmptyApi() - { - // Given - var client = new TestableHttpClient(TestToken, ""); - - // When/Then - var act = () => client.FetchMetadata(); - act.Should().Throw() - .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataApi}*"); - } - - #endregion - - #region Asynchronous FetchMetadataAsync Tests - - [Fact] - public async Task FetchMetadataAsync_Should_ThrowOnMissingToken() - { - // Given - var client = new TestableHttpClient(null, "localhost:8080"); - - // When/Then - var act = async () => await client.FetchMetadataAsync(); - await act.Should().ThrowAsync() - .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataToken}*"); - } - - [Fact] - public async Task FetchMetadataAsync_Should_ThrowOnMissingApi() - { - // Given - var client = new TestableHttpClient(TestToken, null); - - // When/Then - var act = async () => await client.FetchMetadataAsync(); - await act.Should().ThrowAsync() - .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataApi}*"); - } - - [Fact] - public async Task FetchMetadataAsync_Should_ThrowOnEmptyToken() - { - // Given - var client = new TestableHttpClient("", "localhost:8080"); - - // When/Then - var act = async () => await client.FetchMetadataAsync(); - await act.Should().ThrowAsync() - .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataToken}*"); - } - - [Fact] - public async Task FetchMetadataAsync_Should_ThrowOnEmptyApi() - { - // Given - var client = new TestableHttpClient(TestToken, ""); - - // When/Then - var act = async () => await client.FetchMetadataAsync(); - await act.Should().ThrowAsync() - .WithMessage($"*{LambdaMetadataHttpClient.EnvMetadataApi}*"); - } - - [Fact] - public async Task FetchMetadataAsync_Should_SupportCancellation() - { - // Given - var cts = new CancellationTokenSource(); - cts.Cancel(); - var client = new TestableHttpClient(TestToken, "localhost:8080"); - - // When/Then - var act = async () => await client.FetchMetadataAsync(cts.Token); - await act.Should().ThrowAsync(); - } - - #endregion - - #region Environment Variable Tests - - [Fact] - public void GetEnvironmentVariable_Should_ReturnCorrectValues() - { - // Given - var client = new TestableHttpClient(TestToken, "localhost:9000"); - - // When/Then - client.TestGetEnvironmentVariable(LambdaMetadataHttpClient.EnvMetadataToken) - .Should().Be(TestToken); - client.TestGetEnvironmentVariable(LambdaMetadataHttpClient.EnvMetadataApi) - .Should().Be("localhost:9000"); - client.TestGetEnvironmentVariable("UNKNOWN_VAR") - .Should().BeNull(); - } - - #endregion - - /// - /// Testable HTTP client that allows overriding environment variables. - /// - private class TestableHttpClient : LambdaMetadataHttpClient - { - private readonly string? _token; - private readonly string? _api; - - public TestableHttpClient(string? token, string? api) - { - _token = token; - _api = api; - } - - internal override string? GetEnvironmentVariable(string name) - { - return name switch - { - EnvMetadataToken => _token, - EnvMetadataApi => _api, - _ => null - }; - } - - public string? TestGetEnvironmentVariable(string name) => GetEnvironmentVariable(name); - } -} diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/MetadataFetcherTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/MetadataFetcherTests.cs new file mode 100644 index 00000000..82e2f233 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/MetadataFetcherTests.cs @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.Metadata.Exceptions; +using AWS.Lambda.Powertools.Metadata.Internal; +using FluentAssertions; +using Xunit; + +namespace AWS.Lambda.Powertools.Metadata.Tests.Internal; + +public class MetadataFetcherTests +{ + [Fact] + public void Fetch_ThrowsWhenTokenMissing() + { + // Arrange + Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_TOKEN", null); + Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_API", "localhost:8080"); + + try + { + var fetcher = new MetadataFetcher(); + var act = () => fetcher.Fetch(); + act.Should().Throw() + .WithMessage("*AWS_LAMBDA_METADATA_TOKEN*"); + } + finally + { + Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_API", null); + } + } + + [Fact] + public void Fetch_ThrowsWhenApiMissing() + { + // Arrange + Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_TOKEN", "test-token"); + Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_API", null); + + try + { + var fetcher = new MetadataFetcher(); + var act = () => fetcher.Fetch(); + act.Should().Throw() + .WithMessage("*AWS_LAMBDA_METADATA_API*"); + } + finally + { + Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_TOKEN", null); + } + } +} diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientConcurrencyTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientConcurrencyTests.cs deleted file mode 100644 index 7163810c..00000000 --- a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientConcurrencyTests.cs +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using AWS.Lambda.Powertools.Metadata.Internal; -using FluentAssertions; -using NSubstitute; -using Xunit; - -namespace AWS.Lambda.Powertools.Metadata.Tests; - -[Collection("LambdaMetadataClient")] -public class LambdaMetadataClientConcurrencyTests : IDisposable -{ - private readonly ILambdaMetadataHttpClient _mockHttpClient; - - public LambdaMetadataClientConcurrencyTests() - { - _mockHttpClient = Substitute.For(); - LambdaMetadataClient.SetHttpClient(_mockHttpClient); - } - - public void Dispose() - { - LambdaMetadataClient.ResetCache(); - } - - [Fact] - public async Task Get_Should_BeThreadSafe() - { - // Given - var metadata = new LambdaMetadata("use1-az1"); - _mockHttpClient.FetchMetadata().Returns(metadata); - - const int threadCount = 50; - var startSignal = new TaskCompletionSource(); - var tasks = new List>(); - - // When - all threads try to get metadata simultaneously - for (var i = 0; i < threadCount; i++) - { - tasks.Add(Task.Run(async () => - { - await startSignal.Task; - return LambdaMetadataClient.Get(); - })); - } - - startSignal.SetResult(true); - var results = await Task.WhenAll(tasks); - - // Then - all threads should get the same instance - var firstResult = results[0]; - foreach (var result in results) - { - result.Should().BeSameAs(firstResult); - result.AvailabilityZoneId.Should().Be("use1-az1"); - } - - // Should only fetch once despite concurrent access - _mockHttpClient.Received(1).FetchMetadata(); - } - - [Fact] - public async Task GetAsync_Should_BeThreadSafe() - { - // Given - var metadata = new LambdaMetadata("use1-az2"); - _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata); - - const int threadCount = 50; - var startSignal = new TaskCompletionSource(); - var tasks = new List>(); - - // When - all tasks try to get metadata simultaneously - for (var i = 0; i < threadCount; i++) - { - tasks.Add(Task.Run(async () => - { - await startSignal.Task; - return await LambdaMetadataClient.GetAsync(); - })); - } - - startSignal.SetResult(true); - var results = await Task.WhenAll(tasks); - - // Then - all tasks should get the same instance - var firstResult = results[0]; - foreach (var result in results) - { - result.Should().BeSameAs(firstResult); - result.AvailabilityZoneId.Should().Be("use1-az2"); - } - - // Should only fetch once despite concurrent access - await _mockHttpClient.Received(1).FetchMetadataAsync(Arg.Any()); - } - - [Fact] - public async Task MixedSyncAndAsync_Should_BeThreadSafe() - { - // Given - var metadata = new LambdaMetadata("use1-az3"); - _mockHttpClient.FetchMetadata().Returns(metadata); - _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata); - - const int threadCount = 25; - var startSignal = new TaskCompletionSource(); - var syncTasks = new List>(); - var asyncTasks = new List>(); - - // When - mix of sync and async calls - for (var i = 0; i < threadCount; i++) - { - syncTasks.Add(Task.Run(async () => - { - await startSignal.Task; - return LambdaMetadataClient.Get(); - })); - - asyncTasks.Add(Task.Run(async () => - { - await startSignal.Task; - return await LambdaMetadataClient.GetAsync(); - })); - } - - startSignal.SetResult(true); - - var syncResults = await Task.WhenAll(syncTasks); - var asyncResults = await Task.WhenAll(asyncTasks); - - // Then - all should get the same instance - var allResults = syncResults.Concat(asyncResults).ToList(); - var firstResult = allResults[0]; - - foreach (var result in allResults) - { - result.Should().BeSameAs(firstResult); - result.AvailabilityZoneId.Should().Be("use1-az3"); - } - } - - [Fact] - public async Task ConcurrentRefresh_Should_BeThreadSafe() - { - // Given - var callCount = 0; - _mockHttpClient.FetchMetadata().Returns(_ => - { - var count = Interlocked.Increment(ref callCount); - return new LambdaMetadata($"use1-az{count}"); - }); - - const int threadCount = 10; - var startSignal = new TaskCompletionSource(); - var tasks = new List>(); - - // When - concurrent refresh calls - for (var i = 0; i < threadCount; i++) - { - tasks.Add(Task.Run(async () => - { - await startSignal.Task; - return LambdaMetadataClient.Refresh(); - })); - } - - startSignal.SetResult(true); - var results = await Task.WhenAll(tasks); - - // Then - all results should be valid (not null) - foreach (var result in results) - { - result.Should().NotBeNull(); - result.AvailabilityZoneId.Should().StartWith("use1-az"); - } - } - - [Fact] - public async Task ConcurrentRefreshAsync_Should_BeThreadSafe() - { - // Given - var callCount = 0; - _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(_ => - { - var count = Interlocked.Increment(ref callCount); - return Task.FromResult(new LambdaMetadata($"use1-az{count}")); - }); - - const int threadCount = 10; - var startSignal = new TaskCompletionSource(); - var tasks = new List>(); - - // When - concurrent async refresh calls - for (var i = 0; i < threadCount; i++) - { - tasks.Add(Task.Run(async () => - { - await startSignal.Task; - return await LambdaMetadataClient.RefreshAsync(); - })); - } - - startSignal.SetResult(true); - var results = await Task.WhenAll(tasks); - - // Then - all results should be valid (not null) - foreach (var result in results) - { - result.Should().NotBeNull(); - result.AvailabilityZoneId.Should().StartWith("use1-az"); - } - } -} diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientTests.cs deleted file mode 100644 index 34509bdb..00000000 --- a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientTests.cs +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -using AWS.Lambda.Powertools.Metadata.Exceptions; -using AWS.Lambda.Powertools.Metadata.Internal; -using FluentAssertions; -using NSubstitute; -using Xunit; - -namespace AWS.Lambda.Powertools.Metadata.Tests; - -[Collection("LambdaMetadataClient")] -public class LambdaMetadataClientTests : IDisposable -{ - private readonly ILambdaMetadataHttpClient _mockHttpClient; - - public LambdaMetadataClientTests() - { - _mockHttpClient = Substitute.For(); - LambdaMetadataClient.SetHttpClient(_mockHttpClient); - } - - public void Dispose() - { - LambdaMetadataClient.ResetCache(); - } - - #region Synchronous Get Tests - - [Fact] - public void Get_Should_ReturnMetadata() - { - // Given - var metadata = new LambdaMetadata("use1-az1"); - _mockHttpClient.FetchMetadata().Returns(metadata); - - // When - var result = LambdaMetadataClient.Get(); - - // Then - result.Should().NotBeNull(); - result.AvailabilityZoneId.Should().Be("use1-az1"); - } - - [Fact] - public void Get_Should_CacheMetadata() - { - // Given - var metadata = new LambdaMetadata("use1-az1"); - _mockHttpClient.FetchMetadata().Returns(metadata); - - // When - var first = LambdaMetadataClient.Get(); - var second = LambdaMetadataClient.Get(); - - // Then - first.Should().BeSameAs(second); - _mockHttpClient.Received(1).FetchMetadata(); - } - - [Fact] - public void Get_Should_ThrowExceptionOnError() - { - // Given - _mockHttpClient.FetchMetadata().Returns(_ => throw new LambdaMetadataException("Test error")); - - // When/Then - var act = () => LambdaMetadataClient.Get(); - act.Should().Throw() - .WithMessage("Test error"); - } - - [Fact] - public void Get_Should_ThrowExceptionWithStatusCode() - { - // Given - _mockHttpClient.FetchMetadata().Returns(_ => throw new LambdaMetadataException("Server error", 500)); - - // When/Then - var act = () => LambdaMetadataClient.Get(); - act.Should().Throw() - .Where(e => e.StatusCode == 500); - } - - #endregion - - #region Asynchronous GetAsync Tests - - [Fact] - public async Task GetAsync_Should_ReturnMetadata() - { - // Given - var metadata = new LambdaMetadata("use1-az1"); - _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata); - - // When - var result = await LambdaMetadataClient.GetAsync(); - - // Then - result.Should().NotBeNull(); - result.AvailabilityZoneId.Should().Be("use1-az1"); - } - - [Fact] - public async Task GetAsync_Should_CacheMetadata() - { - // Given - var metadata = new LambdaMetadata("use1-az1"); - _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata); - - // When - var first = await LambdaMetadataClient.GetAsync(); - var second = await LambdaMetadataClient.GetAsync(); - - // Then - first.Should().BeSameAs(second); - await _mockHttpClient.Received(1).FetchMetadataAsync(Arg.Any()); - } - - [Fact] - public async Task GetAsync_Should_ThrowExceptionOnError() - { - // Given - _mockHttpClient.FetchMetadataAsync(Arg.Any()) - .Returns(_ => throw new LambdaMetadataException("Test error")); - - // When/Then - var act = async () => await LambdaMetadataClient.GetAsync(); - await act.Should().ThrowAsync() - .WithMessage("Test error"); - } - - [Fact] - public async Task GetAsync_Should_SupportCancellation() - { - // Given - var cts = new CancellationTokenSource(); - cts.Cancel(); - - _mockHttpClient.FetchMetadataAsync(Arg.Any()) - .Returns(_ => throw new OperationCanceledException()); - - // When/Then - var act = async () => await LambdaMetadataClient.GetAsync(cts.Token); - await act.Should().ThrowAsync(); - } - - #endregion - - #region Synchronous Refresh Tests - - [Fact] - public void Refresh_Should_FetchNewMetadata() - { - // Given - var metadata1 = new LambdaMetadata("use1-az1"); - var metadata2 = new LambdaMetadata("use1-az2"); - _mockHttpClient.FetchMetadata().Returns(metadata1, metadata2); - - // When - var first = LambdaMetadataClient.Get(); - var refreshed = LambdaMetadataClient.Refresh(); - - // Then - first.AvailabilityZoneId.Should().Be("use1-az1"); - refreshed.AvailabilityZoneId.Should().Be("use1-az2"); - _mockHttpClient.Received(2).FetchMetadata(); - } - - [Fact] - public void Refresh_Should_UpdateCache() - { - // Given - var metadata1 = new LambdaMetadata("use1-az1"); - var metadata2 = new LambdaMetadata("use1-az2"); - _mockHttpClient.FetchMetadata().Returns(metadata1, metadata2); - - // When - LambdaMetadataClient.Get(); - var refreshed = LambdaMetadataClient.Refresh(); - var afterRefresh = LambdaMetadataClient.Get(); - - // Then - refreshed.Should().BeSameAs(afterRefresh); - refreshed.AvailabilityZoneId.Should().Be("use1-az2"); - } - - #endregion - - #region Asynchronous RefreshAsync Tests - - [Fact] - public async Task RefreshAsync_Should_FetchNewMetadata() - { - // Given - var metadata1 = new LambdaMetadata("use1-az1"); - var metadata2 = new LambdaMetadata("use1-az2"); - _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata1, metadata2); - - // When - var first = await LambdaMetadataClient.GetAsync(); - var refreshed = await LambdaMetadataClient.RefreshAsync(); - - // Then - first.AvailabilityZoneId.Should().Be("use1-az1"); - refreshed.AvailabilityZoneId.Should().Be("use1-az2"); - await _mockHttpClient.Received(2).FetchMetadataAsync(Arg.Any()); - } - - [Fact] - public async Task RefreshAsync_Should_SupportCancellation() - { - // Given - var cts = new CancellationTokenSource(); - cts.Cancel(); - - _mockHttpClient.FetchMetadataAsync(Arg.Any()) - .Returns(_ => throw new OperationCanceledException()); - - // When/Then - var act = async () => await LambdaMetadataClient.RefreshAsync(cts.Token); - await act.Should().ThrowAsync(); - } - - #endregion - - #region Cache Reset Tests - - [Fact] - public void ResetCache_Should_InvalidateCache() - { - // Given - var metadata1 = new LambdaMetadata("use1-az1"); - var metadata2 = new LambdaMetadata("use1-az2"); - _mockHttpClient.FetchMetadata().Returns(metadata1, metadata2); - - // When - var first = LambdaMetadataClient.Get(); - LambdaMetadataClient.ResetCache(); - var afterReset = LambdaMetadataClient.Get(); - - // Then - first.AvailabilityZoneId.Should().Be("use1-az1"); - afterReset.AvailabilityZoneId.Should().Be("use1-az2"); - _mockHttpClient.Received(2).FetchMetadata(); - } - - [Fact] - public void ResetCache_Should_AllowNewFetch() - { - // Given - var metadata = new LambdaMetadata("use1-az1"); - _mockHttpClient.FetchMetadata().Returns(metadata); - - // When - LambdaMetadataClient.Get(); - LambdaMetadataClient.ResetCache(); - LambdaMetadataClient.Get(); - - // Then - _mockHttpClient.Received(2).FetchMetadata(); - } - - #endregion - - #region Mixed Sync/Async Tests - - [Fact] - public async Task Get_And_GetAsync_Should_ShareCache() - { - // Given - var metadata = new LambdaMetadata("use1-az1"); - _mockHttpClient.FetchMetadata().Returns(metadata); - - // When - sync first - var syncResult = LambdaMetadataClient.Get(); - var asyncResult = await LambdaMetadataClient.GetAsync(); - - // Then - syncResult.Should().BeSameAs(asyncResult); - _mockHttpClient.Received(1).FetchMetadata(); - await _mockHttpClient.DidNotReceive().FetchMetadataAsync(Arg.Any()); - } - - [Fact] - public async Task GetAsync_And_Get_Should_ShareCache() - { - // Given - var metadata = new LambdaMetadata("use1-az1"); - _mockHttpClient.FetchMetadataAsync(Arg.Any()).Returns(metadata); - - // When - async first - var asyncResult = await LambdaMetadataClient.GetAsync(); - var syncResult = LambdaMetadataClient.Get(); - - // Then - asyncResult.Should().BeSameAs(syncResult); - await _mockHttpClient.Received(1).FetchMetadataAsync(Arg.Any()); - _mockHttpClient.DidNotReceive().FetchMetadata(); - } - - #endregion -} diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientCollection.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataCollection.cs similarity index 84% rename from libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientCollection.cs rename to libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataCollection.cs index 4b563f05..d8dcbbed 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataClientCollection.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataCollection.cs @@ -18,10 +18,10 @@ namespace AWS.Lambda.Powertools.Metadata.Tests; /// -/// Collection definition to ensure tests that use the static LambdaMetadataClient +/// Collection definition to ensure tests that use the static LambdaMetadata /// run sequentially and don't interfere with each other. /// -[CollectionDefinition("LambdaMetadataClient", DisableParallelization = true)] -public class LambdaMetadataClientCollection +[CollectionDefinition("LambdaMetadata", DisableParallelization = true)] +public class LambdaMetadataCollection { } diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataConcurrencyTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataConcurrencyTests.cs new file mode 100644 index 00000000..5abff27f --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataConcurrencyTests.cs @@ -0,0 +1,70 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using AWS.Lambda.Powertools.Metadata.Internal; +using FluentAssertions; +using NSubstitute; +using Xunit; + +namespace AWS.Lambda.Powertools.Metadata.Tests; + +[Collection("LambdaMetadata")] +public class LambdaMetadataConcurrencyTests : IDisposable +{ + private readonly IMetadataFetcher _mockFetcher; + + public LambdaMetadataConcurrencyTests() + { + _mockFetcher = Substitute.For(); + LambdaMetadata.SetFetcher(_mockFetcher); + } + + public void Dispose() + { + LambdaMetadata.Reset(); + } + + [Fact] + public async Task AvailabilityZoneId_IsThreadSafe() + { + // Arrange + _mockFetcher.Fetch().Returns(new MetadataValues("use1-az1")); + + const int threadCount = 50; + var startSignal = new TaskCompletionSource(); + var tasks = new List>(); + + // Act + for (var i = 0; i < threadCount; i++) + { + tasks.Add(Task.Run(async () => + { + await startSignal.Task; + return LambdaMetadata.AvailabilityZoneId; + })); + } + + startSignal.SetResult(true); + var results = await Task.WhenAll(tasks); + + // Assert + foreach (var result in results) + { + result.Should().Be("use1-az1"); + } + + _mockFetcher.Received(1).Fetch(); + } +} diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataTests.cs index d3c4583b..025da45f 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/LambdaMetadataTests.cs @@ -13,60 +13,109 @@ * permissions and limitations under the License. */ -using System.Text.Json; +using AWS.Lambda.Powertools.Metadata.Exceptions; using AWS.Lambda.Powertools.Metadata.Internal; using FluentAssertions; +using NSubstitute; using Xunit; namespace AWS.Lambda.Powertools.Metadata.Tests; -public class LambdaMetadataTests +[Collection("LambdaMetadata")] +public class LambdaMetadataTests : IDisposable { + private readonly IMetadataFetcher _mockFetcher; + + public LambdaMetadataTests() + { + _mockFetcher = Substitute.For(); + LambdaMetadata.SetFetcher(_mockFetcher); + } + + public void Dispose() + { + LambdaMetadata.Reset(); + } + + [Fact] + public void AvailabilityZoneId_ReturnsValue() + { + // Arrange + _mockFetcher.Fetch().Returns(new MetadataValues("use1-az1")); + + // Act + var result = LambdaMetadata.AvailabilityZoneId; + + // Assert + result.Should().Be("use1-az1"); + } + + [Fact] + public void AvailabilityZoneId_CachesValue() + { + // Arrange + _mockFetcher.Fetch().Returns(new MetadataValues("use1-az1")); + + // Act + var first = LambdaMetadata.AvailabilityZoneId; + var second = LambdaMetadata.AvailabilityZoneId; + + // Assert + first.Should().Be(second); + _mockFetcher.Received(1).Fetch(); + } + [Fact] - public void DefaultConstructor_Should_CreateInstanceWithNullValues() + public void AvailabilityZoneId_ThrowsOnError() { - // When - var metadata = new LambdaMetadata(); + // Arrange + _mockFetcher.Fetch().Returns(_ => throw new LambdaMetadataException("Test error")); - // Then - metadata.AvailabilityZoneId.Should().BeNull(); + // Act & Assert + var act = () => LambdaMetadata.AvailabilityZoneId; + act.Should().Throw().WithMessage("Test error"); } [Fact] - public void Constructor_WithAvailabilityZoneId_Should_SetValue() + public void AvailabilityZoneId_ThrowsWithStatusCode() { - // When - var metadata = new LambdaMetadata("use1-az1"); + // Arrange + _mockFetcher.Fetch().Returns(_ => throw new LambdaMetadataException("Server error", 500)); - // Then - metadata.AvailabilityZoneId.Should().Be("use1-az1"); + // Act & Assert + var act = () => LambdaMetadata.AvailabilityZoneId; + act.Should().Throw().Where(e => e.StatusCode == 500); } [Fact] - public void Deserialize_Should_MapJsonProperty() + public void Refresh_FetchesNewValue() { - // Given - var json = """{"AvailabilityZoneID": "euw1-az3"}"""; + // Arrange + _mockFetcher.Fetch().Returns( + new MetadataValues("use1-az1"), + new MetadataValues("use1-az2")); - // When - var metadata = JsonSerializer.Deserialize(json, LambdaMetadataSerializerContext.Default.LambdaMetadata); + // Act + var first = LambdaMetadata.AvailabilityZoneId; + LambdaMetadata.Refresh(); + var second = LambdaMetadata.AvailabilityZoneId; - // Then - metadata.Should().NotBeNull(); - metadata!.AvailabilityZoneId.Should().Be("euw1-az3"); + // Assert + first.Should().Be("use1-az1"); + second.Should().Be("use1-az2"); + _mockFetcher.Received(2).Fetch(); } [Fact] - public void Deserialize_Should_IgnoreUnknownFields() + public void AvailabilityZoneId_ReturnsNullWhenNotSet() { - // Given - var json = """{"AvailabilityZoneID": "apne1-az1", "UnknownField": "value", "AnotherField": 123}"""; + // Arrange + _mockFetcher.Fetch().Returns(new MetadataValues(null)); - // When - var metadata = JsonSerializer.Deserialize(json, LambdaMetadataSerializerContext.Default.LambdaMetadata); + // Act + var result = LambdaMetadata.AvailabilityZoneId; - // Then - metadata.Should().NotBeNull(); - metadata!.AvailabilityZoneId.Should().Be("apne1-az1"); + // Assert + result.Should().BeNull(); } } From bc4dba8be6c5b6bfcf3f39fefd1a1ae47dc14182 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Wed, 18 Mar 2026 19:07:25 +0000 Subject: [PATCH 3/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Henrique Graca <999396+hjgraca@users.noreply.github.com> --- docs/utilities/metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/utilities/metadata.md b/docs/utilities/metadata.md index 2534c02c..b3c90018 100644 --- a/docs/utilities/metadata.md +++ b/docs/utilities/metadata.md @@ -54,7 +54,7 @@ catch (LambdaMetadataException ex) { Console.WriteLine($"Failed to get metadata: {ex.Message}"); - if (ex.StatusCode.HasValue) + if (ex.StatusCode != -1) Console.WriteLine($"HTTP Status: {ex.StatusCode}"); } ``` From a449f66483258b8317b27cb65776c0548b6358ef Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Wed, 18 Mar 2026 19:08:05 +0000 Subject: [PATCH 4/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Henrique Graca <999396+hjgraca@users.noreply.github.com> --- docs/utilities/metadata.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/utilities/metadata.md b/docs/utilities/metadata.md index b3c90018..e648b110 100644 --- a/docs/utilities/metadata.md +++ b/docs/utilities/metadata.md @@ -36,9 +36,9 @@ public class Function ## Available metadata -| Property | Type | Description | -|-----------------------|----------|----------------------------------------------------------| -| `AvailabilityZoneId` | `string` | The AZ where the function is running (e.g., `use1-az1`) | +| Property | Type | Description | +|-----------------------|-----------|--------------------------------------------------------------------------| +| `AvailabilityZoneId` | `string?` | The AZ where the function is running (e.g., `use1-az1`), or `null` when unavailable | ## Error handling From b1608212cb3bff65b48ae445ba277f652bb6f251 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Wed, 18 Mar 2026 19:08:20 +0000 Subject: [PATCH 5/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Henrique Graca <999396+hjgraca@users.noreply.github.com> --- libraries/src/AWS.Lambda.Powertools.Metadata/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/src/AWS.Lambda.Powertools.Metadata/README.md b/libraries/src/AWS.Lambda.Powertools.Metadata/README.md index 3881a9c1..1e42af4f 100644 --- a/libraries/src/AWS.Lambda.Powertools.Metadata/README.md +++ b/libraries/src/AWS.Lambda.Powertools.Metadata/README.md @@ -38,7 +38,7 @@ try { var azId = LambdaMetadata.AvailabilityZoneId; } -catch (LambdaMetadataException ex) +catch (AWS.Lambda.Powertools.Metadata.Exceptions.LambdaMetadataException ex) { Console.WriteLine($"Failed: {ex.Message}"); } From 8555a72077826f4d2e32064c978be7b81d11f47b Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Wed, 18 Mar 2026 19:37:01 +0000 Subject: [PATCH 6/6] test(metadata): improve MetadataFetcher code coverage to 100% --- .../Internal/MetadataFetcherTests.cs | 166 +++++++++++++++--- 1 file changed, 142 insertions(+), 24 deletions(-) diff --git a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/MetadataFetcherTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/MetadataFetcherTests.cs index 82e2f233..70af3c06 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/MetadataFetcherTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Metadata.Tests/Internal/MetadataFetcherTests.cs @@ -13,6 +13,7 @@ * permissions and limitations under the License. */ +using System.Net; using AWS.Lambda.Powertools.Metadata.Exceptions; using AWS.Lambda.Powertools.Metadata.Internal; using FluentAssertions; @@ -20,45 +21,162 @@ namespace AWS.Lambda.Powertools.Metadata.Tests.Internal; -public class MetadataFetcherTests +public class MetadataFetcherTests : IDisposable { + public MetadataFetcherTests() + { + Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_TOKEN", "test-token"); + Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_API", "localhost:8080"); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_TOKEN", null); + Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_API", null); + } + [Fact] public void Fetch_ThrowsWhenTokenMissing() { - // Arrange Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_TOKEN", null); - Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_API", "localhost:8080"); - try - { - var fetcher = new MetadataFetcher(); - var act = () => fetcher.Fetch(); - act.Should().Throw() - .WithMessage("*AWS_LAMBDA_METADATA_TOKEN*"); - } - finally - { - Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_API", null); - } + var fetcher = new MetadataFetcher(); + var act = () => fetcher.Fetch(); + act.Should().Throw() + .WithMessage("*AWS_LAMBDA_METADATA_TOKEN*"); } [Fact] public void Fetch_ThrowsWhenApiMissing() { - // Arrange - Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_TOKEN", "test-token"); Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_API", null); - try + var fetcher = new MetadataFetcher(); + var act = () => fetcher.Fetch(); + act.Should().Throw() + .WithMessage("*AWS_LAMBDA_METADATA_API*"); + } + + [Fact] + public void Fetch_ReturnsMetadata_WhenSuccessful() + { + var handler = new MockHandler(new HttpResponseMessage(HttpStatusCode.OK) { - var fetcher = new MetadataFetcher(); - var act = () => fetcher.Fetch(); - act.Should().Throw() - .WithMessage("*AWS_LAMBDA_METADATA_API*"); - } - finally + Content = new StringContent("{\"AvailabilityZoneID\":\"use1-az1\"}") + }); + var fetcher = new MetadataFetcher(new HttpClient(handler)); + + var result = fetcher.Fetch(); + + result.AvailabilityZoneId.Should().Be("use1-az1"); + } + + [Fact] + public void Fetch_ThrowsWithStatusCode_WhenHttpError() + { + var handler = new MockHandler(new HttpResponseMessage(HttpStatusCode.Forbidden) { - Environment.SetEnvironmentVariable("AWS_LAMBDA_METADATA_TOKEN", null); + Content = new StringContent("access denied") + }); + var fetcher = new MetadataFetcher(new HttpClient(handler)); + + var act = () => fetcher.Fetch(); + + act.Should().Throw() + .Where(e => e.Message.Contains("403") && e.Message.Contains("access denied")) + .Where(e => e.StatusCode == 403); + } + + [Fact] + public void Fetch_WrapsGenericException() + { + var handler = new MockHandler(new InvalidOperationException("connection refused")); + var fetcher = new MetadataFetcher(new HttpClient(handler)); + + var act = () => fetcher.Fetch(); + + act.Should().Throw() + .WithMessage("*connection refused*") + .WithInnerException(); + } + + [Fact] + public void Fetch_ThrowsWhenDeserializationReturnsNull() + { + var handler = new MockHandler(new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("null") + }); + var fetcher = new MetadataFetcher(new HttpClient(handler)); + + var act = () => fetcher.Fetch(); + + act.Should().Throw() + .WithMessage("*deserialize*"); + } + + [Fact] + public void Fetch_SetsAuthorizationHeader() + { + var handler = new MockHandler(new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("{\"AvailabilityZoneID\":\"use1-az1\"}") + }); + var fetcher = new MetadataFetcher(new HttpClient(handler)); + + fetcher.Fetch(); + + handler.LastRequest.Should().NotBeNull(); + handler.LastRequest!.Headers.Authorization.Should().NotBeNull(); + handler.LastRequest.Headers.Authorization!.Scheme.Should().Be("Bearer"); + handler.LastRequest.Headers.Authorization.Parameter.Should().Be("test-token"); + } + + [Fact] + public void Fetch_UsesCorrectUrl() + { + var handler = new MockHandler(new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("{\"AvailabilityZoneID\":\"use1-az1\"}") + }); + var fetcher = new MetadataFetcher(new HttpClient(handler)); + + fetcher.Fetch(); + + handler.LastRequest!.RequestUri!.ToString() + .Should().Be("http://localhost:8080/2026-01-15/metadata/execution-environment"); + } + + [Fact] + public void Fetch_RethrowsLambdaMetadataException() + { + var handler = new MockHandler(new LambdaMetadataException("original error")); + var fetcher = new MetadataFetcher(new HttpClient(handler)); + + var act = () => fetcher.Fetch(); + + act.Should().Throw() + .WithMessage("original error") + .Where(e => e.InnerException == null); + } + + private class MockHandler : HttpMessageHandler + { + private readonly HttpResponseMessage? _response; + private readonly Exception? _exception; + public HttpRequestMessage? LastRequest { get; private set; } + + public MockHandler(HttpResponseMessage response) => _response = response; + public MockHandler(Exception exception) => _exception = exception; + + protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) + { + LastRequest = request; + if (_exception is not null) throw _exception; + return _response!; } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + => Task.FromResult(Send(request, cancellationToken)); } }