diff --git a/generator/.DevConfigs/433a9a6d-b8ea-4676-b763-70711e8288e4.json b/generator/.DevConfigs/433a9a6d-b8ea-4676-b763-70711e8288e4.json
new file mode 100644
index 000000000000..166d9469d903
--- /dev/null
+++ b/generator/.DevConfigs/433a9a6d-b8ea-4676-b763-70711e8288e4.json
@@ -0,0 +1,11 @@
+{
+ "services": [
+ {
+ "serviceName": "S3",
+ "type": "patch",
+ "changeLogMessages": [
+ "Added PutObjectResponse to TransferUtilityUploadResponse mapping"
+ ]
+ }
+ ]
+}
diff --git a/generator/ServiceClientGeneratorLib/Generators/SourceFiles/AssemblyInfo.cs b/generator/ServiceClientGeneratorLib/Generators/SourceFiles/AssemblyInfo.cs
index ecf1a83dd482..156c2b897efe 100644
--- a/generator/ServiceClientGeneratorLib/Generators/SourceFiles/AssemblyInfo.cs
+++ b/generator/ServiceClientGeneratorLib/Generators/SourceFiles/AssemblyInfo.cs
@@ -15,7 +15,7 @@ namespace ServiceClientGenerator.Generators.SourceFiles
/// Class to produce the template output
///
- #line 1 "C:\codebase\v4\aws-sdk-net-v4\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ #line 1 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")]
public partial class AssemblyInfo : BaseGenerator
{
@@ -36,35 +36,35 @@ public override string TransformText()
// associated with an assembly.
[assembly: AssemblyTitle(""");
- #line 12 "C:\codebase\v4\aws-sdk-net-v4\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ #line 12 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(this.Config.AssemblyTitle));
#line default
#line hidden
this.Write("\")]\r\n#if BCL\r\n[assembly: AssemblyDescription(\"");
- #line 14 "C:\codebase\v4\aws-sdk-net-v4\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ #line 14 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(this.Config.AssemblyDescription(versionIdentifier: "4.7.2")));
#line default
#line hidden
this.Write("\")]\r\n#elif NETSTANDARD20\r\n[assembly: AssemblyDescription(\"");
- #line 16 "C:\codebase\v4\aws-sdk-net-v4\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ #line 16 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(this.Config.AssemblyDescription(versionIdentifier: "NetStandard 2.0")));
#line default
#line hidden
this.Write("\")]\r\n#elif NETCOREAPP3_1\r\n[assembly: AssemblyDescription(\"");
- #line 18 "C:\codebase\v4\aws-sdk-net-v4\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ #line 18 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(this.Config.AssemblyDescription(versionIdentifier: ".NET Core 3.1")));
#line default
#line hidden
this.Write("\")]\r\n#elif NET8_0\r\n[assembly: AssemblyDescription(\"");
- #line 20 "C:\codebase\v4\aws-sdk-net-v4\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ #line 20 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(this.Config.AssemblyDescription(versionIdentifier: ".NET 8.0")));
#line default
@@ -72,7 +72,7 @@ public override string TransformText()
this.Write("\")]\r\n#else\r\n#error Unknown platform constant - unable to set correct AssemblyDesc" +
"ription\r\n#endif\r\n\r\n");
- #line 25 "C:\codebase\v4\aws-sdk-net-v4\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ #line 25 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
if (this.Config.AssemblyTitle=="AWSSDK.DynamoDBv2") {
#line default
@@ -81,7 +81,22 @@ public override string TransformText()
[assembly: InternalsVisibleTo(""AWSSDK.UnitTests.NetFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100db5f59f098d27276c7833875a6263a3cc74ab17ba9a9df0b52aedbe7252745db7274d5271fd79c1f08f668ecfa8eaab5626fa76adc811d3c8fc55859b0d09d3bc0a84eecd0ba891f2b8a2fc55141cdcc37c2053d53491e650a479967c3622762977900eddbf1252ed08a2413f00a28f3a0752a81203f03ccb7f684db373518b4"")]
");
- #line 28 "C:\codebase\v4\aws-sdk-net-v4\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ #line 28 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ }
+
+ #line default
+ #line hidden
+
+ #line 29 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ if (this.Config.AssemblyTitle=="AWSSDK.S3") {
+
+ #line default
+ #line hidden
+ this.Write(@"[assembly: InternalsVisibleTo(""AWSSDK.UnitTests.S3.NetFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100db5f59f098d27276c7833875a6263a3cc74ab17ba9a9df0b52aedbe7252745db7274d5271fd79c1f08f668ecfa8eaab5626fa76adc811d3c8fc55859b0d09d3bc0a84eecd0ba891f2b8a2fc55141cdcc37c2053d53491e650a479967c3622762977900eddbf1252ed08a2413f00a28f3a0752a81203f03ccb7f684db373518b4"")]
+[assembly: InternalsVisibleTo(""AWSSDK.UnitTests.NetFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100db5f59f098d27276c7833875a6263a3cc74ab17ba9a9df0b52aedbe7252745db7274d5271fd79c1f08f668ecfa8eaab5626fa76adc811d3c8fc55859b0d09d3bc0a84eecd0ba891f2b8a2fc55141cdcc37c2053d53491e650a479967c3622762977900eddbf1252ed08a2413f00a28f3a0752a81203f03ccb7f684db373518b4"")]
+");
+
+ #line 32 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
}
#line default
@@ -110,14 +125,14 @@ public override string TransformText()
// [assembly: AssemblyVersion(""1.0.*"")]
[assembly: AssemblyVersion(""");
- #line 51 "C:\codebase\v4\aws-sdk-net-v4\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ #line 55 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(this.Config.ServiceVersion));
#line default
#line hidden
this.Write("\")]\r\n[assembly: AssemblyFileVersion(\"");
- #line 52 "C:\codebase\v4\aws-sdk-net-v4\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
+ #line 56 "C:\dev\repos\aws-sdk-net\generator\ServiceClientGeneratorLib\Generators\SourceFiles\AssemblyInfo.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(this.Config.ServiceFileVersion));
#line default
diff --git a/generator/ServiceClientGeneratorLib/Generators/SourceFiles/AssemblyInfo.tt b/generator/ServiceClientGeneratorLib/Generators/SourceFiles/AssemblyInfo.tt
index 4a8b9fad751a..ab2cf5d21a23 100644
--- a/generator/ServiceClientGeneratorLib/Generators/SourceFiles/AssemblyInfo.tt
+++ b/generator/ServiceClientGeneratorLib/Generators/SourceFiles/AssemblyInfo.tt
@@ -26,6 +26,10 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("AWSSDK.UnitTests.DynamoDBv2.NetFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100db5f59f098d27276c7833875a6263a3cc74ab17ba9a9df0b52aedbe7252745db7274d5271fd79c1f08f668ecfa8eaab5626fa76adc811d3c8fc55859b0d09d3bc0a84eecd0ba891f2b8a2fc55141cdcc37c2053d53491e650a479967c3622762977900eddbf1252ed08a2413f00a28f3a0752a81203f03ccb7f684db373518b4")]
[assembly: InternalsVisibleTo("AWSSDK.UnitTests.NetFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100db5f59f098d27276c7833875a6263a3cc74ab17ba9a9df0b52aedbe7252745db7274d5271fd79c1f08f668ecfa8eaab5626fa76adc811d3c8fc55859b0d09d3bc0a84eecd0ba891f2b8a2fc55141cdcc37c2053d53491e650a479967c3622762977900eddbf1252ed08a2413f00a28f3a0752a81203f03ccb7f684db373518b4")]
<# } #>
+<# if (this.Config.AssemblyTitle=="AWSSDK.S3") { #>
+[assembly: InternalsVisibleTo("AWSSDK.UnitTests.S3.NetFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100db5f59f098d27276c7833875a6263a3cc74ab17ba9a9df0b52aedbe7252745db7274d5271fd79c1f08f668ecfa8eaab5626fa76adc811d3c8fc55859b0d09d3bc0a84eecd0ba891f2b8a2fc55141cdcc37c2053d53491e650a479967c3622762977900eddbf1252ed08a2413f00a28f3a0752a81203f03ccb7f684db373518b4")]
+[assembly: InternalsVisibleTo("AWSSDK.UnitTests.NetFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100db5f59f098d27276c7833875a6263a3cc74ab17ba9a9df0b52aedbe7252745db7274d5271fd79c1f08f668ecfa8eaab5626fa76adc811d3c8fc55859b0d09d3bc0a84eecd0ba891f2b8a2fc55141cdcc37c2053d53491e650a479967c3622762977900eddbf1252ed08a2413f00a28f3a0752a81203f03ccb7f684db373518b4")]
+<# } #>
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("Amazon Web Services SDK for .NET")]
[assembly: AssemblyCompany("Amazon.com, Inc")]
diff --git a/generator/ServiceModels/_manifest.json b/generator/ServiceModels/_manifest.json
index 24252ee5bfd6..5d712b04c8c1 100644
--- a/generator/ServiceModels/_manifest.json
+++ b/generator/ServiceModels/_manifest.json
@@ -60,7 +60,8 @@
"Custom\\Runtime\\TestResponses\\*.txt",
"Custom\\Runtime\\EventStreams\\test_vectors\\*",
"Custom\\Runtime\\TestEndpoints\\*.json",
- "Custom\\TestTools\\ComparerTest.json"
+ "Custom\\TestTools\\ComparerTest.json",
+ "..\\Services\\S3\\UnitTests\\Custom\\EmbeddedResource\\*"
],
"packageReferences": [
{
diff --git a/sdk/src/Services/S3/Custom/Transfer/Internal/ResponseMapper.cs b/sdk/src/Services/S3/Custom/Transfer/Internal/ResponseMapper.cs
new file mode 100644
index 000000000000..d130aee20bff
--- /dev/null
+++ b/sdk/src/Services/S3/Custom/Transfer/Internal/ResponseMapper.cs
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * 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.
+ * *****************************************************************************
+ * __ _ _ ___
+ * ( )( \/\/ )/ __)
+ * /__\ \ / \__ \
+ * (_)(_) \/\/ (___/
+ *
+ * AWS SDK for .NET
+ * API Version: 2006-03-01
+ *
+ */
+
+using Amazon.S3.Model;
+
+namespace Amazon.S3.Transfer.Internal
+{
+ ///
+ /// Utility class for mapping S3 response objects to TransferUtilityUploadResponse.
+ /// Maps fields based on the mapping.json configuration used by the Transfer Utility.
+ ///
+ internal static class ResponseMapper
+ {
+ ///
+ /// Maps a PutObjectResponse to TransferUtilityUploadResponse.
+ /// Uses the field mappings defined in mapping.json "Conversion" -> "PutObjectResponse" -> "UploadResponse".
+ ///
+ /// The PutObjectResponse to map from
+ /// A new TransferUtilityUploadResponse with mapped fields
+ internal static TransferUtilityUploadResponse MapPutObjectResponse(PutObjectResponse source)
+ {
+ if (source == null)
+ return null;
+
+ var response = new TransferUtilityUploadResponse();
+
+ // Map all fields as defined in mapping.json "Conversion" -> "PutObjectResponse" -> "UploadResponse"
+ if (source.IsSetBucketKeyEnabled())
+ response.BucketKeyEnabled = source.BucketKeyEnabled.GetValueOrDefault();
+
+ if (source.IsSetChecksumCRC32())
+ response.ChecksumCRC32 = source.ChecksumCRC32;
+
+ if (source.IsSetChecksumCRC32C())
+ response.ChecksumCRC32C = source.ChecksumCRC32C;
+
+ if (source.IsSetChecksumCRC64NVME())
+ response.ChecksumCRC64NVME = source.ChecksumCRC64NVME;
+
+ if (source.IsSetChecksumSHA1())
+ response.ChecksumSHA1 = source.ChecksumSHA1;
+
+ if (source.IsSetChecksumSHA256())
+ response.ChecksumSHA256 = source.ChecksumSHA256;
+
+ if (source.IsSetChecksumType())
+ response.ChecksumType = source.ChecksumType;
+
+ if (source.IsSetETag())
+ response.ETag = source.ETag;
+
+ if (source.Expiration != null)
+ response.Expiration = source.Expiration;
+
+ if (source.IsSetRequestCharged())
+ response.RequestCharged = source.RequestCharged;
+
+ if (source.ServerSideEncryptionCustomerMethod != null)
+ response.ServerSideEncryptionCustomerMethod = source.ServerSideEncryptionCustomerMethod;
+
+ if (source.ServerSideEncryptionCustomerProvidedKeyMD5 != null)
+ response.ServerSideEncryptionCustomerProvidedKeyMD5 = source.ServerSideEncryptionCustomerProvidedKeyMD5;
+
+ if (source.ServerSideEncryptionKeyManagementServiceEncryptionContext != null)
+ response.ServerSideEncryptionKeyManagementServiceEncryptionContext = source.ServerSideEncryptionKeyManagementServiceEncryptionContext;
+
+ if (source.IsSetServerSideEncryptionKeyManagementServiceKeyId())
+ response.ServerSideEncryptionKeyManagementServiceKeyId = source.ServerSideEncryptionKeyManagementServiceKeyId;
+
+ if (source.ServerSideEncryptionMethod != null)
+ response.ServerSideEncryptionMethod = source.ServerSideEncryptionMethod;
+
+ if (source.IsSetVersionId())
+ response.VersionId = source.VersionId;
+
+ // Copy response metadata
+ response.ResponseMetadata = source.ResponseMetadata;
+ response.ContentLength = source.ContentLength;
+ response.HttpStatusCode = source.HttpStatusCode;
+
+ return response;
+ }
+
+ }
+}
diff --git a/sdk/src/Services/S3/Custom/Transfer/TransferUtilityUploadResponse.cs b/sdk/src/Services/S3/Custom/Transfer/TransferUtilityUploadResponse.cs
new file mode 100644
index 000000000000..3fcc20294a0a
--- /dev/null
+++ b/sdk/src/Services/S3/Custom/Transfer/TransferUtilityUploadResponse.cs
@@ -0,0 +1,470 @@
+/*******************************************************************************
+ * 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.
+ * *****************************************************************************
+ * __ _ _ ___
+ * ( )( \/\/ )/ __)
+ * /__\ \ / \__ \
+ * (_)(_) \/\/ (___/
+ *
+ * AWS SDK for .NET
+ * API Version: 2006-03-01
+ *
+ */
+
+using System;
+using Amazon.Runtime;
+using Amazon.S3.Model;
+using Amazon.Runtime.Internal;
+
+namespace Amazon.S3.Transfer
+{
+ ///
+ /// Response object for Transfer Utility upload operations.
+ /// Contains unified response fields from both simple uploads (PutObjectResponse)
+ /// and multipart uploads (CompleteMultipartUploadResponse).
+ ///
+ public class TransferUtilityUploadResponse : AmazonWebServiceResponse
+ {
+ private bool? _bucketKeyEnabled;
+ private string _checksumCRC32;
+ private string _checksumCRC32C;
+ private string _checksumCRC64NVME;
+ private string _checksumSHA1;
+ private string _checksumSHA256;
+ private ChecksumType _checksumType;
+ private string _etag;
+ private Expiration _expiration;
+ private RequestCharged _requestCharged;
+ private ServerSideEncryptionCustomerMethod _serverSideEncryptionCustomerMethod;
+ private string _sseCustomerKeyMD5;
+ private string _sseKmsEncryptionContext;
+ private string _sseKmsKeyId;
+ private ServerSideEncryptionMethod _serverSideEncryption;
+ private string _versionId;
+
+ ///
+ /// Gets and sets the property BucketKeyEnabled.
+ ///
+ /// Indicates whether the uploaded object uses an S3 Bucket Key for server-side encryption
+ /// with Key Management Service (KMS) keys (SSE-KMS).
+ ///
+ ///
+ public bool? BucketKeyEnabled
+ {
+ get { return this._bucketKeyEnabled; }
+ set { this._bucketKeyEnabled = value; }
+ }
+
+ ///
+ /// Checks if BucketKeyEnabled property is set.
+ ///
+ /// true if BucketKeyEnabled property is set.
+ internal bool IsSetBucketKeyEnabled()
+ {
+ return this._bucketKeyEnabled.HasValue;
+ }
+
+ ///
+ /// Gets and sets the property ChecksumCRC32.
+ ///
+ /// The Base64 encoded, 32-bit CRC-32 checksum of the object. This checksum is only present
+ /// if the checksum was uploaded with the object. When you use an API operation on an object that
+ /// was uploaded using multipart uploads, this value may not be a direct checksum value
+ /// of the full object. Instead, it's a calculation based on the checksum values of each
+ /// individual part. For more information about how checksums are calculated with multipart
+ /// uploads, see
+ /// Checking object integrity in the Amazon S3 User Guide.
+ ///
+ ///
+ public string ChecksumCRC32
+ {
+ get { return this._checksumCRC32; }
+ set { this._checksumCRC32 = value; }
+ }
+
+ ///
+ /// Checks if ChecksumCRC32 property is set.
+ ///
+ /// true if ChecksumCRC32 property is set.
+ internal bool IsSetChecksumCRC32()
+ {
+ return !string.IsNullOrEmpty(this._checksumCRC32);
+ }
+
+ ///
+ /// Gets and sets the property ChecksumCRC32C.
+ ///
+ /// The Base64 encoded, 32-bit CRC-32C checksum of the object. This checksum is only present
+ /// if the checksum was uploaded with the object. When you use an API operation on an object that
+ /// was uploaded using multipart uploads, this value may not be a direct checksum value
+ /// of the full object. Instead, it's a calculation based on the checksum values of each
+ /// individual part. For more information about how checksums are calculated with multipart
+ /// uploads, see
+ /// Checking object integrity in the Amazon S3 User Guide.
+ ///
+ ///
+ public string ChecksumCRC32C
+ {
+ get { return this._checksumCRC32C; }
+ set { this._checksumCRC32C = value; }
+ }
+
+ ///
+ /// Checks if ChecksumCRC32C property is set.
+ ///
+ /// true if ChecksumCRC32C property is set.
+ internal bool IsSetChecksumCRC32C()
+ {
+ return !string.IsNullOrEmpty(this._checksumCRC32C);
+ }
+
+ ///
+ /// Gets and sets the property ChecksumCRC64NVME.
+ ///
+ /// The Base64 encoded, 64-bit CRC-64NVME checksum of the object. This header is present
+ /// if it was uploaded with the CRC-64NVME checksum algorithm, or if it was uploaded
+ /// without a checksum (and Amazon S3 added the default checksum, CRC-64NVME, to the uploaded object).
+ /// For more information about how checksums are calculated with multipart
+ /// uploads, see
+ /// Checking object integrity in the Amazon S3 User Guide.
+ ///
+ ///
+ public string ChecksumCRC64NVME
+ {
+ get { return this._checksumCRC64NVME; }
+ set { this._checksumCRC64NVME = value; }
+ }
+
+ ///
+ /// Checks if ChecksumCRC64NVME property is set.
+ ///
+ /// true if ChecksumCRC64NVME property is set.
+ internal bool IsSetChecksumCRC64NVME()
+ {
+ return !string.IsNullOrEmpty(this._checksumCRC64NVME);
+ }
+
+ ///
+ /// Gets and sets the property ChecksumSHA1.
+ ///
+ /// The Base64 encoded, 160-bit SHA-1 digest of the object. This will only be present
+ /// if it was uploaded with the object. When you use the API operation on an object that
+ /// was uploaded using multipart uploads, this value may not be a direct checksum value
+ /// of the full object. Instead, it's a calculation based on the checksum values of each
+ /// individual part. For more information about how checksums are calculated with multipart
+ /// uploads, see
+ /// Checking object integrity in the Amazon S3 User Guide.
+ ///
+ ///
+ public string ChecksumSHA1
+ {
+ get { return this._checksumSHA1; }
+ set { this._checksumSHA1 = value; }
+ }
+
+ ///
+ /// Checks if ChecksumSHA1 property is set.
+ ///
+ /// true if ChecksumSHA1 property is set.
+ internal bool IsSetChecksumSHA1()
+ {
+ return !string.IsNullOrEmpty(this._checksumSHA1);
+ }
+
+ ///
+ /// Gets and sets the property ChecksumSHA256.
+ ///
+ /// The Base64 encoded, 256-bit SHA-256 digest of the object. This will only be present
+ /// if it was uploaded with the object. When you use an API operation on an object that
+ /// was uploaded using multipart uploads, this value may not be a direct checksum value
+ /// of the full object. Instead, it's a calculation based on the checksum values of each
+ /// individual part. For more information about how checksums are calculated with multipart
+ /// uploads, see
+ /// Checking object integrity in the Amazon S3 User Guide.
+ ///
+ ///
+ public string ChecksumSHA256
+ {
+ get { return this._checksumSHA256; }
+ set { this._checksumSHA256 = value; }
+ }
+
+ ///
+ /// Checks if ChecksumSHA256 property is set.
+ ///
+ /// true if ChecksumSHA256 property is set.
+ internal bool IsSetChecksumSHA256()
+ {
+ return !string.IsNullOrEmpty(this._checksumSHA256);
+ }
+
+ ///
+ /// Gets and sets the property ChecksumType.
+ ///
+ /// This header specifies the checksum type of the object, which determines how part-level
+ /// checksums are combined to create an object-level checksum for multipart objects. For
+ /// PutObject uploads, the checksum type is always FULL_OBJECT. You can use
+ /// this header as a data integrity check to verify that the checksum type that is received
+ /// is the same checksum that was specified. For more information,
+ /// see
+ /// Checking object integrity in the Amazon S3 User Guide.
+ ///
+ ///
+ public ChecksumType ChecksumType
+ {
+ get { return this._checksumType; }
+ set { this._checksumType = value; }
+ }
+
+ ///
+ /// Checks if ChecksumType property is set.
+ ///
+ /// true if ChecksumType property is set.
+ internal bool IsSetChecksumType()
+ {
+ return this._checksumType != null;
+ }
+
+ ///
+ /// Gets and sets the property ETag.
+ ///
+ /// Entity tag for the uploaded object.
+ ///
+ ///
+ ///
+ /// General purpose buckets - To ensure that data is not corrupted traversing
+ /// the network, for objects where the ETag is the MD5 digest of the object, you can calculate
+ /// the MD5 while putting an object to Amazon S3 and compare the returned ETag to the
+ /// calculated MD5 value.
+ ///
+ ///
+ ///
+ /// Directory buckets - The ETag for the object in a directory bucket isn't the
+ /// MD5 digest of the object.
+ ///
+ ///
+ public string ETag
+ {
+ get { return this._etag; }
+ set { this._etag = value; }
+ }
+
+ ///
+ /// Checks if ETag property is set.
+ ///
+ /// true if ETag property is set.
+ internal bool IsSetETag()
+ {
+ return !string.IsNullOrEmpty(this._etag);
+ }
+
+ ///
+ /// Gets and sets the property Expiration.
+ ///
+ /// If the object expiration is configured, this will contain the expiration date (expiry-date)
+ /// and rule ID (rule-id). The value of rule-id is URL encoded.
+ ///
+ ///
+ /// Object expiration information is not returned for directory buckets (for those, the
+ /// response header will contain the value "NotImplemented").
+ ///
+ ///
+ public Expiration Expiration
+ {
+ get { return this._expiration; }
+ set { this._expiration = value; }
+ }
+
+ ///
+ /// Checks if Expiration property is set.
+ ///
+ /// true if Expiration property is set.
+ internal bool IsSetExpiration()
+ {
+ return this._expiration != null;
+ }
+
+ ///
+ /// Gets and sets the property RequestCharged.
+ ///
+ /// If present, indicates that the requester was successfully charged for the request.
+ ///
+ ///
+ public RequestCharged RequestCharged
+ {
+ get { return this._requestCharged; }
+ set { this._requestCharged = value; }
+ }
+
+ ///
+ /// Checks if RequestCharged property is set.
+ ///
+ /// true if RequestCharged property is set.
+ internal bool IsSetRequestCharged()
+ {
+ return this._requestCharged != null;
+ }
+
+ ///
+ /// The Server-side encryption algorithm to be used with the customer provided key.
+ ///
+ ///
+ /// This functionality is not supported for directory buckets.
+ ///
+ ///
+ ///
+ public ServerSideEncryptionCustomerMethod ServerSideEncryptionCustomerMethod
+ {
+ get { return this._serverSideEncryptionCustomerMethod; }
+ set { this._serverSideEncryptionCustomerMethod = value; }
+ }
+
+ ///
+ /// Checks if ServerSideEncryptionCustomerMethod property is set.
+ ///
+ /// true if ServerSideEncryptionCustomerMethod property is set.
+ internal bool IsSetServerSideEncryptionCustomerMethod()
+ {
+ return this._serverSideEncryptionCustomerMethod != null;
+ }
+
+ ///
+ /// The MD5 of the customer encryption key specified in the ServerSideEncryptionCustomerProvidedKey property. The MD5 is
+ /// base 64 encoded. This field is optional, the SDK will calculate the MD5 if this is not set.
+ ///
+ ///
+ /// This functionality is not supported for directory buckets.
+ ///
+ ///
+ ///
+ public string ServerSideEncryptionCustomerProvidedKeyMD5
+ {
+ get { return this._sseCustomerKeyMD5; }
+ set { this._sseCustomerKeyMD5 = value; }
+ }
+
+ ///
+ /// Checks if ServerSideEncryptionCustomerProvidedKeyMD5 property is set.
+ ///
+ /// true if ServerSideEncryptionCustomerProvidedKeyMD5 property is set.
+ internal bool IsSetServerSideEncryptionCustomerProvidedKeyMD5()
+ {
+ return !string.IsNullOrEmpty(this._sseCustomerKeyMD5);
+ }
+
+ ///
+ ///
+ /// If present, indicates the Amazon Web Services KMS Encryption Context to use for object encryption.
+ /// The value of this header is a Base64 encoded string of a UTF-8 encoded JSON, which contains the encryption context as key-value pairs.
+ /// This value is stored as object metadata and automatically gets passed on to Amazon Web Services KMS for future GetObject operations on this object.
+ ///
+ ///
+ [AWSProperty(Sensitive=true)]
+ public string ServerSideEncryptionKeyManagementServiceEncryptionContext
+ {
+ get { return this._sseKmsEncryptionContext; }
+ set { this._sseKmsEncryptionContext = value; }
+ }
+
+ ///
+ /// Checks if ServerSideEncryptionKeyManagementServiceEncryptionContext property is set.
+ ///
+ /// true if ServerSideEncryptionKeyManagementServiceEncryptionContext property is set.
+ internal bool IsSetServerSideEncryptionKeyManagementServiceEncryptionContext()
+ {
+ return !string.IsNullOrEmpty(this._sseKmsEncryptionContext);
+ }
+
+ ///
+ ///
+ /// If present, indicates the ID of the KMS key that was used for object encryption.
+ ///
+ ///
+ [AWSProperty(Sensitive=true)]
+ public string ServerSideEncryptionKeyManagementServiceKeyId
+ {
+ get { return this._sseKmsKeyId; }
+ set { this._sseKmsKeyId = value; }
+ }
+
+ ///
+ /// Checks if ServerSideEncryptionKeyManagementServiceKeyId property is set.
+ ///
+ /// true if ServerSideEncryptionKeyManagementServiceKeyId property is set.
+ internal bool IsSetServerSideEncryptionKeyManagementServiceKeyId()
+ {
+ return !string.IsNullOrEmpty(this._sseKmsKeyId);
+ }
+
+ ///
+ ///
+ /// The server-side encryption algorithm used when you store this object in Amazon S3 or Amazon FSx.
+ ///
+ ///
+ ///
+ /// When accessing data stored in Amazon FSx file systems using S3 access points, the only valid server side encryption option is aws:fsx.
+ ///
+ ///
+ ///
+ public ServerSideEncryptionMethod ServerSideEncryptionMethod
+ {
+ get { return this._serverSideEncryption; }
+ set { this._serverSideEncryption = value; }
+ }
+
+ ///
+ /// Checks if ServerSideEncryptionMethod property is set.
+ ///
+ /// true if ServerSideEncryptionMethod property is set.
+ internal bool IsSetServerSideEncryptionMethod()
+ {
+ return this._serverSideEncryption != null;
+ }
+
+ ///
+ /// Gets and sets the property VersionId.
+ ///
+ /// Version ID of the object.
+ ///
+ ///
+ ///
+ /// If you enable versioning for a bucket, Amazon S3 automatically generates a unique
+ /// version ID for the object being stored. Amazon S3 returns this ID in the response.
+ /// When you enable versioning for a bucket, if Amazon S3 receives multiple write requests
+ /// for the same object simultaneously, it stores all of the objects. For more information
+ /// about versioning, see Adding
+ /// Objects to Versioning-Enabled Buckets in the Amazon S3 User Guide. For
+ /// information about returning the versioning state of a bucket, see GetBucketVersioning.
+ ///
+ ///
+ ///
+ ///
+ /// This functionality is not supported for directory buckets.
+ ///
+ ///
+ ///
+ public string VersionId
+ {
+ get { return this._versionId; }
+ set { this._versionId = value; }
+ }
+
+ ///
+ /// Checks if VersionId property is set.
+ ///
+ /// true if VersionId property is set.
+ internal bool IsSetVersionId()
+ {
+ return !string.IsNullOrEmpty(this._versionId);
+ }
+ }
+}
diff --git a/sdk/src/Services/S3/Properties/AssemblyInfo.cs b/sdk/src/Services/S3/Properties/AssemblyInfo.cs
index 90f36fe7e34e..b8f59159c3e1 100644
--- a/sdk/src/Services/S3/Properties/AssemblyInfo.cs
+++ b/sdk/src/Services/S3/Properties/AssemblyInfo.cs
@@ -19,6 +19,8 @@
#error Unknown platform constant - unable to set correct AssemblyDescription
#endif
+[assembly: InternalsVisibleTo("AWSSDK.UnitTests.S3.NetFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100db5f59f098d27276c7833875a6263a3cc74ab17ba9a9df0b52aedbe7252745db7274d5271fd79c1f08f668ecfa8eaab5626fa76adc811d3c8fc55859b0d09d3bc0a84eecd0ba891f2b8a2fc55141cdcc37c2053d53491e650a479967c3622762977900eddbf1252ed08a2413f00a28f3a0752a81203f03ccb7f684db373518b4")]
+[assembly: InternalsVisibleTo("AWSSDK.UnitTests.NetFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100db5f59f098d27276c7833875a6263a3cc74ab17ba9a9df0b52aedbe7252745db7274d5271fd79c1f08f668ecfa8eaab5626fa76adc811d3c8fc55859b0d09d3bc0a84eecd0ba891f2b8a2fc55141cdcc37c2053d53491e650a479967c3622762977900eddbf1252ed08a2413f00a28f3a0752a81203f03ccb7f684db373518b4")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("Amazon Web Services SDK for .NET")]
[assembly: AssemblyCompany("Amazon.com, Inc")]
diff --git a/sdk/test/Services/S3/UnitTests/AWSSDK.UnitTests.S3.NetFramework.csproj b/sdk/test/Services/S3/UnitTests/AWSSDK.UnitTests.S3.NetFramework.csproj
index 3b58730ba499..9a461a902882 100644
--- a/sdk/test/Services/S3/UnitTests/AWSSDK.UnitTests.S3.NetFramework.csproj
+++ b/sdk/test/Services/S3/UnitTests/AWSSDK.UnitTests.S3.NetFramework.csproj
@@ -75,5 +75,8 @@
+
+
+
\ No newline at end of file
diff --git a/sdk/test/Services/S3/UnitTests/Custom/EmbeddedResource/mapping.json b/sdk/test/Services/S3/UnitTests/Custom/EmbeddedResource/mapping.json
new file mode 100644
index 000000000000..224a0a35dfdb
--- /dev/null
+++ b/sdk/test/Services/S3/UnitTests/Custom/EmbeddedResource/mapping.json
@@ -0,0 +1,291 @@
+{
+ "Definition": {
+ "UploadRequest": {
+ "PutObjectRequest": [
+ "ACL",
+ "Bucket",
+ "BucketKeyEnabled",
+ "CacheControl",
+ "ChecksumAlgorithm",
+ "ChecksumCRC32",
+ "ChecksumCRC32C",
+ "ChecksumCRC64NVME",
+ "ChecksumSHA1",
+ "ChecksumSHA256",
+ "ContentDisposition",
+ "ContentEncoding",
+ "ContentLanguage",
+ "ContentType",
+ "ExpectedBucketOwner",
+ "Expires",
+ "GrantFullControl",
+ "GrantRead",
+ "GrantReadACP",
+ "GrantWriteACP",
+ "IfMatch",
+ "IfNoneMatch",
+ "Key",
+ "Metadata",
+ "ObjectLockLegalHoldStatus",
+ "ObjectLockMode",
+ "ObjectLockRetainUntilDate",
+ "RequestPayer",
+ "SSECustomerAlgorithm",
+ "SSECustomerKey",
+ "SSECustomerKeyMD5",
+ "SSEKMSEncryptionContext",
+ "SSEKMSKeyId",
+ "ServerSideEncryption",
+ "StorageClass",
+ "Tagging",
+ "WebsiteRedirectLocation"
+ ]
+ },
+ "UploadResponse": {
+ "PutObjectResponse": [
+ "BucketKeyEnabled",
+ "ChecksumCRC32",
+ "ChecksumCRC32C",
+ "ChecksumCRC64NVME",
+ "ChecksumSHA1",
+ "ChecksumSHA256",
+ "ChecksumType",
+ "ETag",
+ "Expiration",
+ "RequestCharged",
+ "SSECustomerAlgorithm",
+ "SSECustomerKeyMD5",
+ "SSEKMSEncryptionContext",
+ "SSEKMSKeyId",
+ "ServerSideEncryption",
+ "VersionId"
+ ]
+ },
+ "DownloadRequest": {
+ "GetObjectRequest": [
+ "Bucket",
+ "ChecksumMode",
+ "ExpectedBucketOwner",
+ "IfMatch",
+ "IfModifiedSince",
+ "IfNoneMatch",
+ "IfUnmodifiedSince",
+ "Key",
+ "RequestPayer",
+ "ResponseCacheControl",
+ "ResponseContentDisposition",
+ "ResponseContentEncoding",
+ "ResponseContentLanguage",
+ "ResponseContentType",
+ "ResponseExpires",
+ "SSECustomerAlgorithm",
+ "SSECustomerKey",
+ "SSECustomerKeyMD5",
+ "VersionId"
+ ]
+ },
+ "DownloadResponse": {
+ "GetObjectResponse": [
+ "AcceptRanges",
+ "BucketKeyEnabled",
+ "CacheControl",
+ "ChecksumCRC32",
+ "ChecksumCRC32C",
+ "ChecksumCRC64NVME",
+ "ChecksumSHA1",
+ "ChecksumSHA256",
+ "ChecksumType",
+ "ContentDisposition",
+ "ContentEncoding",
+ "ContentLanguage",
+ "ContentLength",
+ "ContentRange",
+ "ContentType",
+ "DeleteMarker",
+ "ETag",
+ "Expiration",
+ "Expires",
+ "LastModified",
+ "Metadata",
+ "MissingMeta",
+ "ObjectLockLegalHoldStatus",
+ "ObjectLockMode",
+ "ObjectLockRetainUntilDate",
+ "PartsCount",
+ "ReplicationStatus",
+ "RequestCharged",
+ "Restore",
+ "SSECustomerAlgorithm",
+ "SSECustomerKeyMD5",
+ "SSEKMSKeyId",
+ "ServerSideEncryption",
+ "StorageClass",
+ "TagCount",
+ "VersionId",
+ "WebsiteRedirectLocation"
+ ]
+ }
+ },
+ "Conversion": {
+ "UploadRequest": {
+ "PutObjectRequest": [
+ "Bucket",
+ "ChecksumAlgorithm",
+ "ChecksumCRC32",
+ "ChecksumCRC32C",
+ "ChecksumCRC64NVME",
+ "ChecksumSHA1",
+ "ChecksumSHA256",
+ "ExpectedBucketOwner",
+ "Key",
+ "RequestPayer",
+ "SSECustomerAlgorithm",
+ "SSECustomerKey",
+ "SSECustomerKeyMD5"
+ ],
+ "CreateMultipartRequest": [
+ "ACL",
+ "Bucket",
+ "BucketKeyEnabled",
+ "CacheControl",
+ "ChecksumAlgorithm",
+ "ContentDisposition",
+ "ContentEncoding",
+ "ContentLanguage",
+ "ContentType",
+ "ExpectedBucketOwner",
+ "Expires",
+ "GrantFullControl",
+ "GrantRead",
+ "GrantReadACP",
+ "GrantWriteACP",
+ "Key",
+ "Metadata",
+ "ObjectLockLegalHoldStatus",
+ "ObjectLockMode",
+ "ObjectLockRetainUntilDate",
+ "RequestPayer",
+ "SSECustomerAlgorithm",
+ "SSECustomerKey",
+ "SSECustomerKeyMD5",
+ "SSEKMSEncryptionContext",
+ "SSEKMSKeyId",
+ "ServerSideEncryption",
+ "StorageClass",
+ "Tagging",
+ "WebsiteRedirectLocation"
+ ],
+ "UploadPartRequest": [
+ "Bucket",
+ "ChecksumAlgorithm",
+ "ExpectedBucketOwner",
+ "Key",
+ "RequestPayer",
+ "SSECustomerAlgorithm",
+ "SSECustomerKey",
+ "SSECustomerKeyMD5"
+ ],
+ "CompleteMultipartRequest": [
+ "Bucket",
+ "ChecksumCRC32",
+ "ChecksumCRC32C",
+ "ChecksumCRC64NVME",
+ "ChecksumSHA1",
+ "ChecksumSHA256",
+ "ExpectedBucketOwner",
+ "IfMatch",
+ "IfNoneMatch",
+ "Key",
+ "RequestPayer",
+ "SSECustomerAlgorithm",
+ "SSECustomerKey",
+ "SSECustomerKeyMD5"
+ ],
+ "AbortMultipartRequest": [
+ "Bucket",
+ "ExpectedBucketOwner",
+ "Key",
+ "RequestPayer"
+ ]
+ },
+ "CompleteMultipartResponse": {
+ "UploadResponse": [
+ "BucketKeyEnabled",
+ "ChecksumCRC32",
+ "ChecksumCRC32C",
+ "ChecksumCRC64NVME",
+ "ChecksumSHA1",
+ "ChecksumSHA256",
+ "ChecksumType",
+ "ETag",
+ "Expiration",
+ "RequestCharged",
+ "SSEKMSKeyId",
+ "ServerSideEncryption",
+ "VersionId"
+ ]
+ },
+ "PutObjectResponse": {
+ "UploadResponse": [
+ "BucketKeyEnabled",
+ "ChecksumCRC32",
+ "ChecksumCRC32C",
+ "ChecksumCRC64NVME",
+ "ChecksumSHA1",
+ "ChecksumSHA256",
+ "ChecksumType",
+ "ETag",
+ "Expiration",
+ "RequestCharged",
+ "SSECustomerAlgorithm",
+ "SSECustomerKeyMD5",
+ "SSEKMSEncryptionContext",
+ "SSEKMSKeyId",
+ "ServerSideEncryption",
+ "VersionId"
+ ]
+ },
+ "GetObjectResponse": {
+ "DownloadResponse": [
+ "AcceptRanges",
+ "BucketKeyEnabled",
+ "CacheControl",
+ "ChecksumCRC32",
+ "ChecksumCRC32C",
+ "ChecksumCRC64NVME",
+ "ChecksumSHA1",
+ "ChecksumSHA256",
+ "ChecksumType",
+ "ContentDisposition",
+ "ContentEncoding",
+ "ContentLanguage",
+ "ContentLength",
+ "ContentRange",
+ "ContentType",
+ "DeleteMarker",
+ "ETag",
+ "Expiration",
+ "Expires",
+ "ExpiresString",
+ "LastModified",
+ "Metadata",
+ "MissingMeta",
+ "ObjectLockLegalHoldStatus",
+ "ObjectLockMode",
+ "ObjectLockRetainUntilDate",
+ "PartsCount",
+ "ReplicationStatus",
+ "RequestCharged",
+ "Restore",
+ "SSECustomerAlgorithm",
+ "SSECustomerKeyMD5",
+ "SSEKMSKeyId",
+ "ServerSideEncryption",
+ "StorageClass",
+ "TagCount",
+ "VersionId",
+ "WebsiteRedirectLocation"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/sdk/test/Services/S3/UnitTests/Custom/EmbeddedResource/property-aliases.json b/sdk/test/Services/S3/UnitTests/Custom/EmbeddedResource/property-aliases.json
new file mode 100644
index 000000000000..245790b5fdbe
--- /dev/null
+++ b/sdk/test/Services/S3/UnitTests/Custom/EmbeddedResource/property-aliases.json
@@ -0,0 +1,10 @@
+{
+ "PropertyAliases": {
+ "SSECustomerAlgorithm": "ServerSideEncryptionCustomerMethod",
+ "SSECustomerKeyMD5": "ServerSideEncryptionCustomerProvidedKeyMD5",
+ "SSEKMSKeyId": "ServerSideEncryptionKeyManagementServiceKeyId",
+ "ServerSideEncryption": "ServerSideEncryptionMethod",
+ "SSEKMSEncryptionContext": "ServerSideEncryptionKeyManagementServiceEncryptionContext",
+ "Restore": "RestoreExpiration"
+ }
+}
diff --git a/sdk/test/Services/S3/UnitTests/Custom/ResponseMapperTests.cs b/sdk/test/Services/S3/UnitTests/Custom/ResponseMapperTests.cs
new file mode 100644
index 000000000000..ce4960e85985
--- /dev/null
+++ b/sdk/test/Services/S3/UnitTests/Custom/ResponseMapperTests.cs
@@ -0,0 +1,458 @@
+/*
+ * 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 Amazon.Runtime;
+using Amazon.S3;
+using Amazon.S3.Model;
+using Amazon.S3.Transfer;
+using Amazon.S3.Transfer.Internal;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using System.Text.Json;
+
+namespace AWSSDK.UnitTests
+{
+ [TestClass]
+ public class ResponseMapperTests
+ {
+ private static JsonDocument _mappingJson;
+ private static JsonDocument _propertyAliasesJson;
+ private static Dictionary _propertyAliases;
+
+ [ClassInitialize]
+ public static void ClassInitialize(TestContext context)
+ {
+ // Read mapping.json using robust resource loading (same pattern as Utils.cs)
+ using (var stream = GetResourceStream("mapping.json"))
+ {
+ if (stream == null)
+ {
+ throw new FileNotFoundException("Could not find embedded resource: mapping.json");
+ }
+
+ using (var reader = new StreamReader(stream))
+ {
+ var jsonContent = reader.ReadToEnd();
+ _mappingJson = JsonDocument.Parse(jsonContent);
+ }
+ }
+
+ // Read property-aliases.json using robust resource loading
+ using (var stream = GetResourceStream("property-aliases.json"))
+ {
+ if (stream != null)
+ {
+ using (var reader = new StreamReader(stream))
+ {
+ var aliasContent = reader.ReadToEnd();
+ _propertyAliasesJson = JsonDocument.Parse(aliasContent);
+
+ // Convert to dictionary for fast lookup
+ _propertyAliases = new Dictionary();
+ var aliasesElement = _propertyAliasesJson.RootElement.GetProperty("PropertyAliases");
+ foreach (var alias in aliasesElement.EnumerateObject())
+ {
+ _propertyAliases[alias.Name] = alias.Value.GetString();
+ }
+ }
+ }
+ else
+ {
+ _propertyAliases = new Dictionary();
+ }
+ }
+ }
+
+ ///
+ /// Gets embedded resource stream using partial name matching (same pattern as Utils.cs)
+ ///
+ private static Stream GetResourceStream(string resourceName)
+ {
+ Assembly assembly = Assembly.GetExecutingAssembly();
+ var resource = FindResourceName(assembly, resourceName);
+ if(resource == null)
+ {
+ assembly = Assembly.GetCallingAssembly();
+ resource = FindResourceName(assembly, resourceName);
+ }
+
+ return resource != null ? assembly.GetManifestResourceStream(resource) : null;
+ }
+
+ ///
+ /// Finds resource name using case-insensitive partial matching (same pattern as Utils.cs)
+ ///
+ private static string FindResourceName(Assembly assembly, string partialName)
+ {
+ var resources = FindResourceName(assembly, s => s.IndexOf(partialName, StringComparison.OrdinalIgnoreCase) >= 0);
+ return resources.FirstOrDefault();
+ }
+
+ ///
+ /// Finds resource names matching predicate (same pattern as Utils.cs)
+ ///
+ private static IEnumerable FindResourceName(Assembly assembly, Predicate match)
+ {
+ var allResources = assembly.GetManifestResourceNames();
+ foreach (var resource in allResources)
+ {
+ if (match(resource))
+ yield return resource;
+ }
+ }
+
+ [ClassCleanup]
+ public static void ClassCleanup()
+ {
+ _mappingJson?.Dispose();
+ _propertyAliasesJson?.Dispose();
+ }
+
+ [TestMethod]
+ [TestCategory("S3")]
+ public void MapPutObjectResponse_AllMappedProperties_WorkCorrectly()
+ {
+ // Get the expected mappings from JSON
+ var putObjectMappings = _mappingJson.RootElement
+ .GetProperty("Conversion")
+ .GetProperty("PutObjectResponse")
+ .GetProperty("UploadResponse")
+ .EnumerateArray()
+ .Select(prop => prop.GetString())
+ .ToList();
+
+ // Create source object with dynamically generated test data
+ var sourceResponse = new PutObjectResponse();
+ var sourceType = typeof(PutObjectResponse);
+ var testDataValues = new Dictionary();
+
+ // Generate test data for each mapped property
+ foreach (var propertyName in putObjectMappings)
+ {
+ // Resolve alias to actual property name
+ var resolvedPropertyName = ResolvePropertyName(propertyName);
+ var sourceProperty = sourceType.GetProperty(resolvedPropertyName);
+ if (sourceProperty?.CanWrite == true)
+ {
+ var testValue = GenerateTestValue(sourceProperty.PropertyType, propertyName);
+ sourceProperty.SetValue(sourceResponse, testValue);
+ testDataValues[propertyName] = testValue;
+ }
+ }
+
+ // Add inherited properties for comprehensive testing
+ sourceResponse.HttpStatusCode = HttpStatusCode.OK;
+ sourceResponse.ContentLength = 1024;
+
+ // Map the response
+ var mappedResponse = ResponseMapper.MapPutObjectResponse(sourceResponse);
+ Assert.IsNotNull(mappedResponse, "Mapped response should not be null");
+
+ // Verify all mapped properties using reflection
+ var targetType = typeof(TransferUtilityUploadResponse);
+ var failedAssertions = new List();
+
+ foreach (var propertyName in putObjectMappings)
+ {
+ // Resolve alias to actual property name for reflection lookups
+ var resolvedPropertyName = ResolvePropertyName(propertyName);
+ var sourceProperty = sourceType.GetProperty(resolvedPropertyName);
+ var targetProperty = targetType.GetProperty(resolvedPropertyName);
+
+ if (sourceProperty == null)
+ {
+ failedAssertions.Add($"Source property '{propertyName}' (resolved to: {resolvedPropertyName}) not found in PutObjectResponse");
+ continue;
+ }
+
+ if (targetProperty == null)
+ {
+ failedAssertions.Add($"Target property '{propertyName}' (resolved to: {resolvedPropertyName}) not found in TransferUtilityUploadResponse");
+ continue;
+ }
+
+ var sourceValue = sourceProperty.GetValue(sourceResponse);
+ var targetValue = targetProperty.GetValue(mappedResponse);
+
+ // Special handling for complex object comparisons
+ if (!AreValuesEqual(sourceValue, targetValue))
+ {
+ failedAssertions.Add($"{propertyName}: Expected '{sourceValue ?? "null"}', got '{targetValue ?? "null"}'");
+ }
+ }
+
+ // Test inherited properties
+ Assert.AreEqual(sourceResponse.HttpStatusCode, mappedResponse.HttpStatusCode, "HttpStatusCode should match");
+ Assert.AreEqual(sourceResponse.ContentLength, mappedResponse.ContentLength, "ContentLength should match");
+
+ // Report any failures
+ if (failedAssertions.Any())
+ {
+ Assert.Fail($"Property mapping failures:\n{string.Join("\n", failedAssertions)}");
+ }
+ }
+
+ [TestMethod]
+ [TestCategory("S3")]
+ public void MapPutObjectResponse_NullValues_HandledCorrectly()
+ {
+ // Test null handling scenarios
+ var testCases = new[]
+ {
+ // Test null Expiration
+ new PutObjectResponse { Expiration = null },
+
+ // Test null enum conversions
+ new PutObjectResponse { ChecksumType = null, RequestCharged = null, ServerSideEncryptionMethod = null }
+ };
+
+ foreach (var testCase in testCases)
+ {
+ var mapped = ResponseMapper.MapPutObjectResponse(testCase);
+ Assert.IsNotNull(mapped, "Response should always be mappable");
+
+ // Test null handling
+ if (testCase.Expiration == null)
+ {
+ Assert.IsNull(mapped.Expiration, "Null Expiration should map to null");
+ }
+ }
+ }
+
+ [TestMethod]
+ [TestCategory("S3")]
+ public void ValidateTransferUtilityUploadResponseDefinitionCompleteness()
+ {
+ ValidateResponseDefinitionCompleteness(
+ new[] { "Definition", "UploadResponse", "PutObjectResponse" },
+ "TransferUtilityUploadResponse");
+ }
+
+ [TestMethod]
+ [TestCategory("S3")]
+ public void ValidateCompleteMultipartUploadResponseConversionCompleteness()
+ {
+ ValidateResponseDefinitionCompleteness(
+ new[] { "Conversion", "CompleteMultipartResponse", "UploadResponse" },
+ "TransferUtilityUploadResponse");
+ }
+
+ // Uncomment for DOTNET-8277
+
+ // [TestMethod]
+ // [TestCategory("S3")]
+ // public void ValidatePutObjectRequestDefinitionCompleteness()
+ // {
+ // ValidateResponseDefinitionCompleteness(
+ // new[] { "Definition", "UploadRequest", "PutObjectRequest" },
+ // "PutObjectRequest");
+ // }
+
+ // [TestMethod]
+ // [TestCategory("S3")]
+ // public void ValidateGetObjectRequestDefinitionCompleteness()
+ // {
+ // ValidateResponseDefinitionCompleteness(
+ // new[] { "Definition", "DownloadRequest", "GetObjectRequest" },
+ // "GetObjectRequest");
+ // }
+
+ // [TestMethod]
+ // [TestCategory("S3")]
+ // public void ValidateGetObjectRequestDefinitionCompleteness()
+ // {
+ // ValidateResponseDefinitionCompleteness(
+ // new[] { "Definition", "DownloadRequest", "GetObjectRequest" },
+ // "TransferUtilityDownloadRequest");
+ // }
+
+ ///
+ /// Generates appropriate test data for a given property type
+ ///
+ private static object GenerateTestValue(Type propertyType, string propertyName)
+ {
+ // Handle nullable types
+ if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ var underlyingType = Nullable.GetUnderlyingType(propertyType);
+ return GenerateTestValue(underlyingType, propertyName);
+ }
+
+ // String properties
+ if (propertyType == typeof(string))
+ {
+ return $"test-{propertyName.ToLower()}";
+ }
+
+ // Boolean properties
+ if (propertyType == typeof(bool))
+ {
+ return true;
+ }
+
+ // Enum properties
+ if (propertyType.IsEnum)
+ {
+ // For all enums, use the first available value
+ var enumValues = Enum.GetValues(propertyType);
+ return enumValues.Length > 0 ? enumValues.GetValue(0) :
+ throw new InvalidOperationException($"Enum {propertyType.Name} has no values");
+ }
+
+ // AWS SDK ConstantClass properties (like ChecksumType, RequestCharged, etc.)
+ if (typeof(ConstantClass).IsAssignableFrom(propertyType))
+ {
+ // Use reflection to get static readonly fields that are of the same type
+ var constantFields = propertyType.GetFields(BindingFlags.Public | BindingFlags.Static)
+ .Where(f => f.IsStatic && f.IsInitOnly && f.FieldType == propertyType);
+
+ var firstConstant = constantFields.FirstOrDefault();
+ return firstConstant?.GetValue(null) ??
+ throw new InvalidOperationException($"ConstantClass {propertyType.Name} has no static constants");
+ }
+
+ // Special object types
+ if (propertyType == typeof(Expiration))
+ {
+ return new Expiration
+ {
+ ExpiryDate = DateTime.UtcNow.AddDays(30),
+ RuleId = "test-expiration-rule"
+ };
+ }
+
+ // Integer types
+ if (propertyType == typeof(int) || propertyType == typeof(long))
+ {
+ return 1024;
+ }
+
+ // For unknown types, throw an exception instead of returning null
+ // If we've reached this point it means there is an unhandled scenario/missing mapping in our test code that we need to handle.
+ throw new NotSupportedException(
+ $"GenerateTestValue does not support type '{propertyType.FullName}' for property '{propertyName}'. " +
+ $"Please add support for this type to ensure comprehensive test coverage.");
+ }
+
+ ///
+ /// Compares two values for equality with special handling for complex objects
+ ///
+ private static bool AreValuesEqual(object sourceValue, object targetValue)
+ {
+ // Both null
+ if (sourceValue == null && targetValue == null)
+ return true;
+
+ // One null, other not
+ if (sourceValue == null || targetValue == null)
+ return false;
+
+ // Special handling for Expiration objects
+ if (sourceValue is Expiration sourceExpiration && targetValue is Expiration targetExpiration)
+ {
+ return sourceExpiration.ExpiryDate == targetExpiration.ExpiryDate &&
+ sourceExpiration.RuleId == targetExpiration.RuleId;
+ }
+
+ // For most cases, use default equality
+ return sourceValue.Equals(targetValue);
+ }
+
+ ///
+ /// Resolves a property name to its actual class property name, checking aliases if needed
+ ///
+ private static string ResolvePropertyName(string propertyName)
+ {
+ // Check if there's an alias for this property name
+ if (_propertyAliases.TryGetValue(propertyName, out var aliasedName))
+ {
+ return aliasedName;
+ }
+
+ // Return the original name if no alias exists
+ return propertyName;
+ }
+
+ ///
+ /// Generic helper method to validate response definition completeness.
+ /// This method ensures that all properties defined in mapping.json actually exist
+ /// in the corresponding AWS SDK response classes, supporting property name aliases
+ /// for backwards compatibility and maintainability.
+ ///
+ private static void ValidateResponseDefinitionCompleteness(
+ string[] jsonPath,
+ string responseTypeName,
+ Func> getAdditionalProperties = null)
+ {
+ // Get direct properties from response class
+ var directProperties = typeof(TResponse)
+ .GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(p => p.CanRead)
+ .Select(p => p.Name)
+ .ToList();
+
+ // Get additional properties if provided (e.g., HeadersCollection properties)
+ var additionalProperties = getAdditionalProperties?.Invoke()?.ToList() ?? new List();
+
+ // Combine direct and additional properties
+ var actualProperties = directProperties.Union(additionalProperties)
+ .OrderBy(name => name)
+ .ToList();
+
+ // Navigate to the JSON definition using the provided path
+ var jsonElement = _mappingJson.RootElement;
+ foreach (var pathSegment in jsonPath)
+ {
+ jsonElement = jsonElement.GetProperty(pathSegment);
+ }
+
+ var definitionProperties = jsonElement
+ .EnumerateArray()
+ .Select(prop => prop.GetString())
+ .OrderBy(name => name)
+ .ToList();
+
+ // Check each definition property, resolving aliases as needed
+ var extraInDefinition = new List();
+
+ foreach (var definitionProperty in definitionProperties)
+ {
+ var resolvedPropertyName = ResolvePropertyName(definitionProperty);
+
+ // Check if the resolved property name exists in the actual class
+ if (!actualProperties.Contains(resolvedPropertyName))
+ {
+ extraInDefinition.Add($"{definitionProperty} (resolved to: {resolvedPropertyName})");
+ }
+ }
+
+ // Assert no extra properties
+ if (extraInDefinition.Any())
+ {
+ var additionalContext = additionalProperties.Any()
+ ? $" or additional properties"
+ : "";
+
+ Assert.Fail($"Definition section contains {extraInDefinition.Count} extra properties that don't exist in the actual {responseTypeName} class{additionalContext}: {string.Join(", ", extraInDefinition)}. " +
+ $"Please verify they exist in the response class{additionalContext}.");
+ }
+ }
+ }
+}
diff --git a/sdk/test/UnitTests/AWSSDK.UnitTests.NetFramework.csproj b/sdk/test/UnitTests/AWSSDK.UnitTests.NetFramework.csproj
index 5d1c7e6f8ba0..b6f3c88dfff9 100644
--- a/sdk/test/UnitTests/AWSSDK.UnitTests.NetFramework.csproj
+++ b/sdk/test/UnitTests/AWSSDK.UnitTests.NetFramework.csproj
@@ -84,6 +84,7 @@
+
\ No newline at end of file