diff --git a/generator/.DevConfigs/252dad9f-d2a9-4d49-bff8-000924f0add4.json b/generator/.DevConfigs/252dad9f-d2a9-4d49-bff8-000924f0add4.json new file mode 100644 index 000000000000..3c1fff65ffab --- /dev/null +++ b/generator/.DevConfigs/252dad9f-d2a9-4d49-bff8-000924f0add4.json @@ -0,0 +1,11 @@ +{ + "services": [ + { + "serviceName": "S3", + "type": "minor", + "changeLogMessages": [ + "Add GetObjectResponse to TransferUtilityDownloadResponse mapping." + ] + } + ] +} \ No newline at end of file diff --git a/sdk/src/Services/S3/Custom/Transfer/Internal/ResponseMapper.cs b/sdk/src/Services/S3/Custom/Transfer/Internal/ResponseMapper.cs index 0eb055d6db1b..2ba493556c35 100644 --- a/sdk/src/Services/S3/Custom/Transfer/Internal/ResponseMapper.cs +++ b/sdk/src/Services/S3/Custom/Transfer/Internal/ResponseMapper.cs @@ -20,6 +20,7 @@ * */ +using System; using System.Collections.Generic; using Amazon.S3.Model; @@ -37,10 +38,11 @@ internal static class ResponseMapper /// /// The PutObjectResponse to map from /// A new TransferUtilityUploadResponse with mapped fields + /// Thrown when source is null internal static TransferUtilityUploadResponse MapPutObjectResponse(PutObjectResponse source) { if (source == null) - return null; + throw new ArgumentNullException(nameof(source)); var response = new TransferUtilityUploadResponse(); @@ -72,10 +74,11 @@ internal static TransferUtilityUploadResponse MapPutObjectResponse(PutObjectResp /// /// The CompleteMultipartUploadResponse to map from /// A new TransferUtilityUploadResponse with mapped fields + /// Thrown when source is null internal static TransferUtilityUploadResponse MapCompleteMultipartUploadResponse(CompleteMultipartUploadResponse source) { if (source == null) - return null; + throw new ArgumentNullException(nameof(source)); var response = new TransferUtilityUploadResponse(); @@ -100,55 +103,92 @@ internal static TransferUtilityUploadResponse MapCompleteMultipartUploadResponse return response; } + /// + /// Private helper method to populate the common properties from GetObjectResponse to the base response class. + /// Contains all the shared mapping logic for GetObjectResponse fields. + /// + /// The GetObjectResponse to map from + /// The TransferUtilityGetObjectResponseBase to populate + /// Thrown when source or target is null + private static void PopulateGetObjectResponseBase(GetObjectResponse source, TransferUtilityGetObjectResponseBase target) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (target == null) + throw new ArgumentNullException(nameof(target)); + + // Map all fields as defined in mapping.json "Conversion" -> "GetObjectResponse" -> "DownloadResponse" + target.AcceptRanges = source.AcceptRanges; + target.BucketKeyEnabled = source.BucketKeyEnabled.GetValueOrDefault(); + target.ChecksumCRC32 = source.ChecksumCRC32; + target.ChecksumCRC32C = source.ChecksumCRC32C; + target.ChecksumCRC64NVME = source.ChecksumCRC64NVME; + target.ChecksumSHA1 = source.ChecksumSHA1; + target.ChecksumSHA256 = source.ChecksumSHA256; + target.ChecksumType = source.ChecksumType; + target.ContentRange = source.ContentRange; + target.Headers = source.Headers; + target.DeleteMarker = source.DeleteMarker; + target.ETag = source.ETag; + target.Expiration = source.Expiration; + target.ExpiresString = source.ExpiresString; + target.LastModified = source.LastModified; + target.Metadata = source.Metadata; + target.MissingMeta = source.MissingMeta; + target.ObjectLockLegalHoldStatus = source.ObjectLockLegalHoldStatus; + target.ObjectLockMode = source.ObjectLockMode; + target.ObjectLockRetainUntilDate = source.ObjectLockRetainUntilDate; + target.PartsCount = source.PartsCount; + target.ReplicationStatus = source.ReplicationStatus; + target.RequestCharged = source.RequestCharged; + target.RestoreExpiration = source.RestoreExpiration; + target.RestoreInProgress = source.RestoreInProgress; + target.ServerSideEncryptionCustomerMethod = source.ServerSideEncryptionCustomerMethod; + target.ServerSideEncryptionCustomerProvidedKeyMD5 = source.ServerSideEncryptionCustomerProvidedKeyMD5; + target.ServerSideEncryptionKeyManagementServiceKeyId = source.ServerSideEncryptionKeyManagementServiceKeyId; + target.ServerSideEncryptionMethod = source.ServerSideEncryptionMethod; + target.StorageClass = source.StorageClass; + target.TagCount = source.TagCount; + target.VersionId = source.VersionId; + target.WebsiteRedirectLocation = source.WebsiteRedirectLocation; + } + /// /// Maps a GetObjectResponse to TransferUtilityDownloadResponse. /// Uses the field mappings defined in mapping.json "Conversion" -> "GetObjectResponse" -> "DownloadResponse". /// /// The GetObjectResponse to map from /// A new TransferUtilityDownloadResponse with mapped fields + /// Thrown when source is null internal static TransferUtilityDownloadResponse MapGetObjectResponse(GetObjectResponse source) { if (source == null) - return null; + throw new ArgumentNullException(nameof(source)); var response = new TransferUtilityDownloadResponse(); - - // Map all fields as defined in mapping.json "Conversion" -> "GetObjectResponse" -> "DownloadResponse" - response.AcceptRanges = source.AcceptRanges; - response.BucketKeyEnabled = source.BucketKeyEnabled.GetValueOrDefault(); - response.ChecksumCRC32 = source.ChecksumCRC32; - response.ChecksumCRC32C = source.ChecksumCRC32C; - response.ChecksumCRC64NVME = source.ChecksumCRC64NVME; - response.ChecksumSHA1 = source.ChecksumSHA1; - response.ChecksumSHA256 = source.ChecksumSHA256; - response.ChecksumType = source.ChecksumType; - response.ContentRange = source.ContentRange; - response.Headers = source.Headers; - response.DeleteMarker = source.DeleteMarker; - response.ETag = source.ETag; - response.Expiration = source.Expiration; - response.ExpiresString = source.ExpiresString; - response.LastModified = source.LastModified; - response.Metadata = source.Metadata; - response.MissingMeta = source.MissingMeta; - response.ObjectLockLegalHoldStatus = source.ObjectLockLegalHoldStatus; - response.ObjectLockMode = source.ObjectLockMode; - response.ObjectLockRetainUntilDate = source.ObjectLockRetainUntilDate; - response.PartsCount = source.PartsCount; - response.ReplicationStatus = source.ReplicationStatus; - response.RequestCharged = source.RequestCharged; - response.RestoreExpiration = source.RestoreExpiration; - response.RestoreInProgress = source.RestoreInProgress; - response.ServerSideEncryptionCustomerMethod = source.ServerSideEncryptionCustomerMethod; - response.ServerSideEncryptionCustomerProvidedKeyMD5 = source.ServerSideEncryptionCustomerProvidedKeyMD5; - response.ServerSideEncryptionKeyManagementServiceKeyId = source.ServerSideEncryptionKeyManagementServiceKeyId; - response.ServerSideEncryptionMethod = source.ServerSideEncryptionMethod; - response.StorageClass = source.StorageClass; - response.TagCount = source.TagCount; - response.VersionId = source.VersionId; - response.WebsiteRedirectLocation = source.WebsiteRedirectLocation; + PopulateGetObjectResponseBase(source, response); return response; } + + /// + /// Maps a GetObjectResponse to TransferUtilityOpenStreamResponse. + /// Uses the same field mappings as DownloadResponse plus the ResponseStream property. + /// + /// The GetObjectResponse to map from + /// A new TransferUtilityOpenStreamResponse with mapped fields + /// Thrown when source is null + internal static TransferUtilityOpenStreamResponse MapGetObjectResponseToOpenStream(GetObjectResponse source) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + var response = new TransferUtilityOpenStreamResponse(); + PopulateGetObjectResponseBase(source, response); + response.ResponseStream = source.ResponseStream; + + return response; + } + } } diff --git a/sdk/src/Services/S3/Custom/Transfer/TransferUtilityDownloadResponse.cs b/sdk/src/Services/S3/Custom/Transfer/TransferUtilityDownloadResponse.cs index 761bb1454146..36474a64c0aa 100644 --- a/sdk/src/Services/S3/Custom/Transfer/TransferUtilityDownloadResponse.cs +++ b/sdk/src/Services/S3/Custom/Transfer/TransferUtilityDownloadResponse.cs @@ -31,270 +31,7 @@ namespace Amazon.S3.Transfer /// Response object for Transfer Utility download operations. /// Contains response metadata from download operations. /// - public class TransferUtilityDownloadResponse + public class TransferUtilityDownloadResponse : TransferUtilityGetObjectResponseBase { - /// - /// Gets and sets the AcceptRanges property. - /// - public string AcceptRanges { get; set; } - - /// - /// Gets and sets the property BucketKeyEnabled. - /// - /// Indicates whether the object uses an S3 Bucket Key for server-side encryption with - /// Amazon Web Services KMS (SSE-KMS). - /// - /// - public bool? BucketKeyEnabled { get; set; } - - /// - /// The collection of headers for the response. - /// - public HeadersCollection Headers { get; set; } - - /// - /// Gets and sets the property ChecksumCRC32. - /// - /// The Base64 encoded, 32-bit CRC-32 checksum of the object. - /// - /// - public string ChecksumCRC32 { get; set; } - - /// - /// Gets and sets the property ChecksumCRC32C. - /// - /// The Base64 encoded, 32-bit CRC-32C checksum of the object. - /// - /// - public string ChecksumCRC32C { get; set; } - - /// - /// Gets and sets the property ChecksumCRC64NVME. - /// - /// The Base64 encoded, 64-bit CRC-64NVME checksum of the object. - /// - /// - public string ChecksumCRC64NVME { get; set; } - - /// - /// Gets and sets the property ChecksumSHA1. - /// - /// The Base64 encoded, 160-bit SHA-1 digest of the object. - /// - /// - public string ChecksumSHA1 { get; set; } - - /// - /// Gets and sets the property ChecksumSHA256. - /// - /// The Base64 encoded, 256-bit SHA-256 checksum of the object. - /// - /// - public string ChecksumSHA256 { get; set; } - - /// - /// Gets and sets the property ChecksumType. - /// - /// The checksum type used to calculate the object-level checksum. - /// - /// - public ChecksumType ChecksumType { get; set; } - - /// - /// Gets and sets the ContentRange property. - /// - public string ContentRange { get; set; } - - /// - /// Gets and sets the DeleteMarker property. - /// - /// Specifies whether the object retrieved was (true) or was not (false) a Delete Marker. - /// - /// - public string DeleteMarker { get; set; } - - /// - /// Gets and sets the ETag property. - /// - /// An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. - /// - /// - public string ETag { get; set; } - - /// - /// Gets and sets the property Expiration. - /// - /// If the object expiration is configured, this will contain the expiration date and rule ID. - /// - /// - public Expiration Expiration { get; set; } - - /// - /// Gets and sets the ExpiresString property. - /// - /// The date and time at which the object is no longer cacheable (string format). - /// - /// - public string ExpiresString { get; set; } - - /// - /// Gets and sets the property LastModified. - /// - /// Date and time when the object was last modified. - /// - /// - public DateTime? LastModified { get; set; } - - /// - /// Gets and sets the Metadata property. - /// - /// The collection of metadata for the object. - /// - /// - public MetadataCollection Metadata { get; set; } - - /// - /// Gets and sets the property MissingMeta. - /// - /// This is set to the number of metadata entries not returned in the headers that are - /// prefixed with x-amz-meta-. - /// - /// - public int? MissingMeta { get; set; } - - /// - /// Gets and sets the property ObjectLockLegalHoldStatus. - /// - /// Indicates whether this object has an active legal hold. - /// - /// - public ObjectLockLegalHoldStatus ObjectLockLegalHoldStatus { get; set; } - - /// - /// Gets and sets the property ObjectLockMode. - /// - /// The Object Lock mode that's currently in place for this object. - /// - /// - public ObjectLockMode ObjectLockMode { get; set; } - - /// - /// Gets and sets the property ObjectLockRetainUntilDate. - /// - /// The date and time when this object's Object Lock will expire. - /// - /// - public DateTime? ObjectLockRetainUntilDate { get; set; } - - /// - /// Gets and sets the PartsCount property. - /// - /// The number of parts this object has. - /// - /// - public int? PartsCount { get; set; } - - /// - /// Gets and sets the property ReplicationStatus. - /// - /// Amazon S3 can return this if your request involves a bucket that is either a source - /// or destination in a replication rule. - /// - /// - public ReplicationStatus ReplicationStatus { get; set; } - - /// - /// Gets and sets the RequestCharged property. - /// - /// If present, indicates that the requester was successfully charged for the request. - /// - /// - public RequestCharged RequestCharged { get; set; } - - /// - /// Gets and sets the RestoreExpiration property. - /// - /// RestoreExpiration will be set for objects that have been restored from Amazon Glacier. - /// It indicates for those objects how long the restored object will exist. - /// - /// - public DateTime? RestoreExpiration { get; set; } - - /// - /// Gets and sets the RestoreInProgress - /// - /// Will be true when the object is in the process of being restored from Amazon Glacier. - /// - /// - /// This functionality is not supported for directory buckets. - /// Only the S3 Express One Zone storage class is supported by directory buckets to store objects. - /// - /// - public bool? RestoreInProgress { get; set; } - - /// - /// Gets and sets the ServerSideEncryptionCustomerMethod property. - /// - /// The server-side encryption algorithm to be used with the customer provided key. - /// - /// - public ServerSideEncryptionCustomerMethod ServerSideEncryptionCustomerMethod { get; set; } - - /// - /// Gets and sets the ServerSideEncryptionCustomerProvidedKeyMD5 property. - /// - /// The MD5 server-side encryption of the customer-provided encryption key. - /// - /// - public string ServerSideEncryptionCustomerProvidedKeyMD5 { get; set; } - - /// - /// Gets and sets the ServerSideEncryptionKeyManagementServiceKeyId property. - /// - /// If present, indicates the ID of the KMS key that was used for object encryption. - /// - /// - public string ServerSideEncryptionKeyManagementServiceKeyId { get; set; } - - /// - /// Gets and sets the ServerSideEncryptionMethod property. - /// - /// The server-side encryption algorithm used when you store this object in Amazon S3. - /// - /// - public ServerSideEncryptionMethod ServerSideEncryptionMethod { get; set; } - - /// - /// Gets and sets the property StorageClass. - /// - /// Provides storage class information of the object. - /// - /// - public S3StorageClass StorageClass { get; set; } - - /// - /// Gets and sets the property TagCount. - /// - /// The number of tags, if any, on the object. - /// - /// - public int TagCount { get; set; } - - /// - /// Gets and sets the property VersionId. - /// - /// Version ID of the object. - /// - /// - public string VersionId { get; set; } - - /// - /// Gets and sets the property WebsiteRedirectLocation. - /// - /// If the bucket is configured as a website, redirects requests for this object to another - /// object in the same bucket or to an external URL. - /// - /// - public string WebsiteRedirectLocation { get; set; } } } diff --git a/sdk/src/Services/S3/Custom/Transfer/TransferUtilityGetObjectResponseBase.cs b/sdk/src/Services/S3/Custom/Transfer/TransferUtilityGetObjectResponseBase.cs new file mode 100644 index 000000000000..431d498afe9e --- /dev/null +++ b/sdk/src/Services/S3/Custom/Transfer/TransferUtilityGetObjectResponseBase.cs @@ -0,0 +1,293 @@ +/******************************************************************************* + * 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 System.Collections.Generic; +using Amazon.Runtime; +using Amazon.S3.Model; + +namespace Amazon.S3.Transfer +{ + /// + /// Base response object for Transfer Utility operations that retrieve S3 object metadata. + /// Contains response metadata from S3 GetObject operations. + /// + public abstract class TransferUtilityGetObjectResponseBase + { + /// + /// Gets and sets the AcceptRanges property. + /// + public string AcceptRanges { get; set; } + + /// + /// Gets and sets the property BucketKeyEnabled. + /// + /// Indicates whether the object uses an S3 Bucket Key for server-side encryption with + /// Amazon Web Services KMS (SSE-KMS). + /// + /// + public bool? BucketKeyEnabled { get; set; } + + /// + /// The collection of headers for the response. + /// + public HeadersCollection Headers { get; set; } + + /// + /// Gets and sets the property ChecksumCRC32. + /// + /// The Base64 encoded, 32-bit CRC-32 checksum of the object. + /// + /// + public string ChecksumCRC32 { get; set; } + + /// + /// Gets and sets the property ChecksumCRC32C. + /// + /// The Base64 encoded, 32-bit CRC-32C checksum of the object. + /// + /// + public string ChecksumCRC32C { get; set; } + + /// + /// Gets and sets the property ChecksumCRC64NVME. + /// + /// The Base64 encoded, 64-bit CRC-64NVME checksum of the object. + /// + /// + public string ChecksumCRC64NVME { get; set; } + + /// + /// Gets and sets the property ChecksumSHA1. + /// + /// The Base64 encoded, 160-bit SHA-1 digest of the object. + /// + /// + public string ChecksumSHA1 { get; set; } + + /// + /// Gets and sets the property ChecksumSHA256. + /// + /// The Base64 encoded, 256-bit SHA-256 checksum of the object. + /// + /// + public string ChecksumSHA256 { get; set; } + + /// + /// Gets and sets the property ChecksumType. + /// + /// The checksum type used to calculate the object-level checksum. + /// + /// + public ChecksumType ChecksumType { get; set; } + + /// + /// Gets and sets the ContentRange property. + /// + public string ContentRange { get; set; } + + /// + /// Gets and sets the DeleteMarker property. + /// + /// Specifies whether the object retrieved was (true) or was not (false) a Delete Marker. + /// + /// + public string DeleteMarker { get; set; } + + /// + /// Gets and sets the ETag property. + /// + /// An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. + /// + /// + public string ETag { get; set; } + + /// + /// Gets and sets the property Expiration. + /// + /// If the object expiration is configured, this will contain the expiration date and rule ID. + /// + /// + public Expiration Expiration { get; set; } + + /// + /// Gets and sets the ExpiresString property. + /// + /// The date and time at which the object is no longer cacheable (string format). + /// + /// + public string ExpiresString { get; set; } + + /// + /// Gets and sets the property LastModified. + /// + /// Date and time when the object was last modified. + /// + /// + public DateTime? LastModified { get; set; } + + /// + /// Gets and sets the Metadata property. + /// + /// The collection of metadata for the object. + /// + /// + public MetadataCollection Metadata { get; set; } + + /// + /// Gets and sets the property MissingMeta. + /// + /// This is set to the number of metadata entries not returned in the headers that are + /// prefixed with x-amz-meta-. + /// + /// + public int? MissingMeta { get; set; } + + /// + /// Gets and sets the property ObjectLockLegalHoldStatus. + /// + /// Indicates whether this object has an active legal hold. + /// + /// + public ObjectLockLegalHoldStatus ObjectLockLegalHoldStatus { get; set; } + + /// + /// Gets and sets the property ObjectLockMode. + /// + /// The Object Lock mode that's currently in place for this object. + /// + /// + public ObjectLockMode ObjectLockMode { get; set; } + + /// + /// Gets and sets the property ObjectLockRetainUntilDate. + /// + /// The date and time when this object's Object Lock will expire. + /// + /// + public DateTime? ObjectLockRetainUntilDate { get; set; } + + /// + /// Gets and sets the PartsCount property. + /// + /// The number of parts this object has. + /// + /// + public int? PartsCount { get; set; } + + /// + /// Gets and sets the property ReplicationStatus. + /// + /// Amazon S3 can return this if your request involves a bucket that is either a source + /// or destination in a replication rule. + /// + /// + public ReplicationStatus ReplicationStatus { get; set; } + + /// + /// Gets and sets the RequestCharged property. + /// + /// If present, indicates that the requester was successfully charged for the request. + /// + /// + public RequestCharged RequestCharged { get; set; } + + /// + /// Gets and sets the RestoreExpiration property. + /// + /// RestoreExpiration will be set for objects that have been restored from Amazon Glacier. + /// It indicates for those objects how long the restored object will exist. + /// + /// + public DateTime? RestoreExpiration { get; set; } + + /// + /// + /// + public bool? RestoreInProgress { get; set; } + + /// + /// Gets and sets the ServerSideEncryptionCustomerMethod property. + /// + /// The server-side encryption algorithm to be used with the customer provided key. + /// + /// + public ServerSideEncryptionCustomerMethod ServerSideEncryptionCustomerMethod { get; set; } + + /// + /// Gets and sets the ServerSideEncryptionCustomerProvidedKeyMD5 property. + /// + /// The MD5 server-side encryption of the customer-provided encryption key. + /// + /// + public string ServerSideEncryptionCustomerProvidedKeyMD5 { get; set; } + + /// + /// Gets and sets the ServerSideEncryptionKeyManagementServiceKeyId property. + /// + /// If present, indicates the ID of the KMS key that was used for object encryption. + /// + /// + public string ServerSideEncryptionKeyManagementServiceKeyId { get; set; } + + /// + /// Gets and sets the ServerSideEncryptionMethod property. + /// + /// The server-side encryption algorithm used when you store this object in Amazon S3. + /// + /// + public ServerSideEncryptionMethod ServerSideEncryptionMethod { get; set; } + + /// + /// Gets and sets the property StorageClass. + /// + /// Provides storage class information of the object. + /// + /// + public S3StorageClass StorageClass { get; set; } + + /// + /// Gets and sets the property TagCount. + /// + /// The number of tags, if any, on the object. + /// + /// + public int? TagCount { get; set; } + + /// + /// Gets and sets the property VersionId. + /// + /// Version ID of the object. + /// + /// + public string VersionId { get; set; } + + /// + /// Gets and sets the property WebsiteRedirectLocation. + /// + /// If the bucket is configured as a website, redirects requests for this object to another + /// object in the same bucket or to an external URL. + /// + /// + public string WebsiteRedirectLocation { get; set; } + } +} diff --git a/sdk/src/Services/S3/Custom/Transfer/TransferUtilityOpenStreamResponse.cs b/sdk/src/Services/S3/Custom/Transfer/TransferUtilityOpenStreamResponse.cs new file mode 100644 index 000000000000..df2f57bce35f --- /dev/null +++ b/sdk/src/Services/S3/Custom/Transfer/TransferUtilityOpenStreamResponse.cs @@ -0,0 +1,97 @@ +/******************************************************************************* + * 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 System.IO; +using Amazon.Runtime; + +namespace Amazon.S3.Transfer +{ + /// + /// Response object for Transfer Utility open stream operations. + /// Contains the stream and response metadata from open stream operations. + /// + public class TransferUtilityOpenStreamResponse : TransferUtilityGetObjectResponseBase, IDisposable + { + private bool disposed; + private Stream responseStream; + + #region Dispose Pattern + + /// + /// Disposes of all managed and unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases the unmanaged resources used by the TransferUtilityOpenStreamResponse and optionally disposes of the managed resources. + /// + /// true to release both managed and unmanaged resources; false to releases only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!this.disposed) + { + if (disposing) + { + // Remove Managed Resources + // I.O.W. remove resources that have to be explicitly + // "Dispose"d or Closed. For an S3 Response, these are: + // 1. The Response Stream for GET Object requests + // 2. The HttpResponse object for GET Object requests + if (responseStream != null) + { + responseStream.Dispose(); + } + } + + responseStream = null; + disposed = true; + } + } + + #endregion + + /// + /// Gets and sets the ResponseStream property. + /// + /// An open stream read from to get the data from S3. In order to + /// use this stream without leaking the underlying resource, please + /// wrap access to the stream within a using block. + /// + /// + public Stream ResponseStream + { + get { return this.responseStream; } + set { this.responseStream = value; } + } + + // Check to see if ResponseStream property is set + internal bool IsSetResponseStream() + { + return this.responseStream != null; + } + } +} diff --git a/sdk/test/Services/S3/UnitTests/Custom/EmbeddedResource/property-aliases.json b/sdk/test/Services/S3/UnitTests/Custom/EmbeddedResource/property-aliases.json index 63216442578e..6e08ac4a05d2 100644 --- a/sdk/test/Services/S3/UnitTests/Custom/EmbeddedResource/property-aliases.json +++ b/sdk/test/Services/S3/UnitTests/Custom/EmbeddedResource/property-aliases.json @@ -132,6 +132,13 @@ "SSEKMSKeyId": "ServerSideEncryptionKeyManagementServiceKeyId", "ServerSideEncryption": "ServerSideEncryptionMethod", "Restore": "RestoreExpiration" + }, + "TransferUtilityOpenStreamResponse": { + "SSECustomerAlgorithm": "ServerSideEncryptionCustomerMethod", + "SSECustomerKeyMD5": "ServerSideEncryptionCustomerProvidedKeyMD5", + "SSEKMSKeyId": "ServerSideEncryptionKeyManagementServiceKeyId", + "ServerSideEncryption": "ServerSideEncryptionMethod", + "Restore": "RestoreExpiration" } } } \ No newline at end of file diff --git a/sdk/test/Services/S3/UnitTests/Custom/ResponseMapperTests.cs b/sdk/test/Services/S3/UnitTests/Custom/ResponseMapperTests.cs index 3243857687a6..acaf3c0084e8 100644 --- a/sdk/test/Services/S3/UnitTests/Custom/ResponseMapperTests.cs +++ b/sdk/test/Services/S3/UnitTests/Custom/ResponseMapperTests.cs @@ -684,6 +684,204 @@ public void ValidateTransferUtilityUploadRequestDefinitionCompleteness() }); } + [TestMethod] + [TestCategory("S3")] + public void MapGetObjectResponseToOpenStream_AllMappedProperties_WorkCorrectly() + { + ValidateMappingTransferUtilityAndSdkRequests( + new[] { "Conversion", "GetObjectResponse", "DownloadResponse" }, + (sourceResponse) => + { + return ResponseMapper.MapGetObjectResponseToOpenStream(sourceResponse); + }, + usesHeadersCollection: true, + (sourceResponse) => + { + sourceResponse.HttpStatusCode = HttpStatusCode.OK; + sourceResponse.ContentLength = 1024; + sourceResponse.ResponseStream = new MemoryStream(new byte[1024]); + }, + (sourceResponse, targetResponse) => + { + Assert.AreSame(sourceResponse.ResponseStream, targetResponse.ResponseStream, "ResponseStream should be the same instance"); + }); + } + + [TestMethod] + [TestCategory("S3")] + public void MapGetObjectResponseToOpenStream_NullValues_HandledCorrectly() + { + // Test null handling scenarios + var testCases = new[] + { + // Test null Expiration + new GetObjectResponse { Expiration = null }, + + // Test null enum conversions + new GetObjectResponse { ChecksumType = null, RequestCharged = null, ServerSideEncryptionMethod = null }, + + // Test null ResponseStream + new GetObjectResponse { ResponseStream = null } + }; + + foreach (var testCase in testCases) + { + var mapped = ResponseMapper.MapGetObjectResponseToOpenStream(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"); + } + + if (testCase.ResponseStream == null) + { + Assert.IsNull(mapped.ResponseStream, "Null ResponseStream should map to null"); + } + } + } + + [TestMethod] + [TestCategory("S3")] + public void MapGetObjectResponseToOpenStream_ResponseStream_HandledCorrectly() + { + // Test with actual stream + var testStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 }); + var sourceResponse = new GetObjectResponse + { + ResponseStream = testStream, + ETag = "test-etag", + Headers = { ContentLength = 5 } + }; + + var mappedResponse = ResponseMapper.MapGetObjectResponseToOpenStream(sourceResponse); + + Assert.IsNotNull(mappedResponse, "Mapped response should not be null"); + Assert.AreSame(testStream, mappedResponse.ResponseStream, "ResponseStream should be the same instance"); + Assert.AreEqual("test-etag", mappedResponse.ETag, "Other properties should also be mapped"); + Assert.AreEqual(5, mappedResponse.Headers.ContentLength, "ContentLength should be mapped"); + + // Test with null stream + var sourceWithNullStream = new GetObjectResponse + { + ResponseStream = null, + ETag = "test-etag-2" + }; + + var mappedWithNullStream = ResponseMapper.MapGetObjectResponseToOpenStream(sourceWithNullStream); + + Assert.IsNotNull(mappedWithNullStream, "Mapped response should not be null even with null stream"); + Assert.IsNull(mappedWithNullStream.ResponseStream, "ResponseStream should be null when source is null"); + Assert.AreEqual("test-etag-2", mappedWithNullStream.ETag, "Other properties should still be mapped"); + } + + [TestMethod] + [TestCategory("S3")] + public void MapGetObjectResponseToOpenStream_NullSource_ThrowsArgumentNullException() + { + Assert.ThrowsException(() => + ResponseMapper.MapGetObjectResponseToOpenStream(null), + "Mapping null source should throw ArgumentNullException"); + } + + [TestMethod] + [TestCategory("S3")] + public void TransferUtilityOpenStreamResponse_Dispose_DisposesResponseStream() + { + // Arrange + var memoryStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 }); + var response = new TransferUtilityOpenStreamResponse + { + ResponseStream = memoryStream, + ETag = "test-etag" + }; + + // Act + response.Dispose(); + + // Assert - accessing disposed stream should throw ObjectDisposedException + Assert.ThrowsException(() => _ = memoryStream.Length, + "Accessing Length of disposed stream should throw ObjectDisposedException"); + Assert.ThrowsException(() => _ = memoryStream.Position, + "Accessing Position of disposed stream should throw ObjectDisposedException"); + Assert.ThrowsException(() => memoryStream.Read(new byte[1], 0, 1), + "Reading from disposed stream should throw ObjectDisposedException"); + Assert.IsNull(response.ResponseStream, "ResponseStream should be null after disposal"); + } + + [TestMethod] + [TestCategory("S3")] + public void TransferUtilityOpenStreamResponse_Dispose_MultipleCallsSafe() + { + // Arrange + var memoryStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 }); + var response = new TransferUtilityOpenStreamResponse + { + ResponseStream = memoryStream + }; + + // Act - call dispose multiple times + response.Dispose(); + response.Dispose(); // Second call should not throw + + // Assert - stream should still be disposed after multiple dispose calls + Assert.ThrowsException(() => _ = memoryStream.Length, + "Stream should remain disposed after multiple dispose calls"); + Assert.ThrowsException(() => memoryStream.Read(new byte[1], 0, 1), + "Stream should remain disposed after multiple dispose calls"); + Assert.IsNull(response.ResponseStream, "ResponseStream should remain null after multiple dispose calls"); + } + + [TestMethod] + [TestCategory("S3")] + public void TransferUtilityOpenStreamResponse_Dispose_NullStreamSafe() + { + // Arrange + var response = new TransferUtilityOpenStreamResponse + { + ResponseStream = null, + ETag = "test-etag" + }; + + // Act & Assert - should not throw + response.Dispose(); + Assert.IsNull(response.ResponseStream, "ResponseStream should remain null"); + } + + [TestMethod] + [TestCategory("S3")] + public void TransferUtilityOpenStreamResponse_UsingStatement_DisposesCorrectly() + { + // Arrange + var memoryStream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 }); + MemoryStream capturedStream = null; + + // Act + using (var response = new TransferUtilityOpenStreamResponse()) + { + response.ResponseStream = memoryStream; + response.ETag = "test-etag"; + capturedStream = memoryStream; + } // Dispose should be called here + + // Assert - stream should be disposed after using block + Assert.ThrowsException(() => _ = capturedStream.Length, + "Stream should be disposed after using block"); + Assert.ThrowsException(() => capturedStream.Read(new byte[1], 0, 1), + "Stream should be disposed after using block"); + } + + [TestMethod] + [TestCategory("S3")] + public void TransferUtilityOpenStreamResponse_ImplementsIDisposable() + { + // Assert + Assert.IsTrue(typeof(IDisposable).IsAssignableFrom(typeof(TransferUtilityOpenStreamResponse)), + "TransferUtilityOpenStreamResponse should implement IDisposable"); + } + + /// /// Generates appropriate test data for a given property type ///