Skip to content

Conversation

@GarrettBeatty
Copy link
Contributor

@GarrettBeatty GarrettBeatty commented Oct 18, 2025

Stacked PRs:


Description

This change creates a new api UploadWithResponseAsync. It is the same as the existing UploadAsync api but it returns the TransferUtilityUploadResponse object.

How

In order to re-use as much as the existing code as possible, I made BaseCommand take in a generic which is the return type of the ExecuteAsync function. All commands UploadCommand, DownloadCommand, etc will pass in their expected return type TransferUtilityUploadResponse, TransferUTilityDownloadResponse, etc.

In order to make this change in one go, I had to create place holder classes for TransferUtilityDownloadDirectoryResponse and other responses. These will eventually be populated in future PRs

Motivation and Context

  1. To adhere to the SEP

Testing

1.dry run - d704b36b-7329-42dc-80e0-525a2f82bbcc pass
2. Integration tests

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • My code follows the code style of this project
  • My change requires a change to the documentation
  • I have updated the documentation accordingly
  • I have read the README document
  • I have added tests to cover my changes
  • All new and existing tests passed

License

  • I confirm that this pull request can be released under the Apache 2 license

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds typed response support to S3 TransferUtility and introduces new UploadWithResponseAsync APIs that return upload metadata (e.g., ETag). Refactors internal command pipeline to a generic BaseCommand pattern and adds response types for upload/download/stream/directory/abort operations.

  • Add UploadWithResponseAsync overloads on TransferUtility returning TransferUtilityUploadResponse
  • Introduce BaseCommand and update internal commands to return typed responses
  • Add response DTOs for directory/download/open stream/abort operations and update dev config to minor

Reviewed Changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
sdk/src/Services/S3/Custom/Transfer/_async/TransferUtility.async.cs Adds UploadWithResponseAsync overloads and wires typed command execution/return.
sdk/src/Services/S3/Custom/Transfer/TransferUtilityUploadDirectoryResponse.cs Adds response DTO for upload directory (placeholder).
sdk/src/Services/S3/Custom/Transfer/TransferUtilityOpenStreamResponse.cs Adds response DTO for open stream (currently empty).
sdk/src/Services/S3/Custom/Transfer/TransferUtilityDownloadResponse.cs Adds response DTO for download (placeholder).
sdk/src/Services/S3/Custom/Transfer/TransferUtilityDownloadDirectoryResponse.cs Adds response DTO for download directory (namespace differs from others).
sdk/src/Services/S3/Custom/Transfer/TransferUtilityAbortMultipartUploadsResponse.cs Adds response DTO for abort multipart uploads (placeholder).
sdk/src/Services/S3/Custom/Transfer/TransferUtility.cs Updates internal GetUploadCommand to typed BaseCommand.
sdk/src/Services/S3/Custom/Transfer/Internal/_bcl+netstandard/UploadDirectoryCommand.cs Returns TransferUtilityUploadDirectoryResponse from ExecuteAsync.
sdk/src/Services/S3/Custom/Transfer/Internal/_bcl+netstandard/DownloadDirectoryCommand.cs Returns TransferUtilityDownloadDirectoryResponse from ExecuteAsync.
sdk/src/Services/S3/Custom/Transfer/Internal/_async/SimpleUploadCommand.async.cs Returns TransferUtilityUploadResponse and fires completed event.
sdk/src/Services/S3/Custom/Transfer/Internal/_async/OpenStreamCommand.async.cs Returns TransferUtilityOpenStreamResponse (currently not carrying stream).
sdk/src/Services/S3/Custom/Transfer/Internal/_async/MultipartUploadCommand.async.cs Returns TransferUtilityUploadResponse; unseekable path returns mapped response.
sdk/src/Services/S3/Custom/Transfer/Internal/_async/DownloadCommand.async.cs Returns TransferUtilityDownloadResponse after download completes.
sdk/src/Services/S3/Custom/Transfer/Internal/_async/BaseCommand.async.cs Makes BaseCommand generic and updates ExecuteCommandAsync signature.
sdk/src/Services/S3/Custom/Transfer/Internal/_async/AbortMultipartUploadsCommand.async.cs Returns TransferUtilityAbortMultipartUploadsResponse from ExecuteAsync.
sdk/src/Services/S3/Custom/Transfer/Internal/UploadDirectoryCommand.cs Type aligns with BaseCommand.
sdk/src/Services/S3/Custom/Transfer/Internal/SimpleUploadCommand.cs Type aligns with BaseCommand.
sdk/src/Services/S3/Custom/Transfer/Internal/OpenStreamCommand.cs Type aligns with BaseCommand; removes Return override.
sdk/src/Services/S3/Custom/Transfer/Internal/MultipartUploadCommand.cs Type aligns with BaseCommand.
sdk/src/Services/S3/Custom/Transfer/Internal/DownloadDirectoryCommand.cs Type aligns with BaseCommand.
sdk/src/Services/S3/Custom/Transfer/Internal/DownloadCommand.cs Type aligns with BaseCommand.
sdk/src/Services/S3/Custom/Transfer/Internal/BaseCommand.cs Converts BaseCommand to generic and removes Return property.
sdk/src/Services/S3/Custom/Transfer/Internal/AbortMultipartUploadsCommand.cs Type aligns with BaseCommand.
generator/.DevConfigs/77d980ad-8f58-4f2e-97f8-d2c8c5ba3732.json Adds dev config (minor) noting new UploadWithResponseAsync APIs and response-mapping enhancements.

@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/3 to gcbeatty/tuuploadresponse October 18, 2025 19:54
GarrettBeatty added a commit that referenced this pull request Oct 18, 2025
stack-info: PR: #4062, branch: GarrettBeatty/stacked/4
@GarrettBeatty GarrettBeatty force-pushed the GarrettBeatty/stacked/4 branch from 21a5f4f to e27d144 Compare October 18, 2025 19:54
@GarrettBeatty GarrettBeatty changed the base branch from gcbeatty/tuuploadresponse to GarrettBeatty/stacked/3 October 18, 2025 19:54
@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/3 to gcbeatty/tuuploadresponse October 19, 2025 18:13
@GarrettBeatty GarrettBeatty force-pushed the GarrettBeatty/stacked/4 branch from e27d144 to c89de03 Compare October 19, 2025 18:13
GarrettBeatty added a commit that referenced this pull request Oct 19, 2025
stack-info: PR: #4062, branch: GarrettBeatty/stacked/4
@GarrettBeatty GarrettBeatty changed the base branch from gcbeatty/tuuploadresponse to GarrettBeatty/stacked/3 October 19, 2025 18:13
@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/3 to gcbeatty/tuuploadresponse October 19, 2025 18:15
@GarrettBeatty GarrettBeatty force-pushed the GarrettBeatty/stacked/4 branch from c89de03 to 8d603df Compare October 19, 2025 18:15
GarrettBeatty added a commit that referenced this pull request Oct 19, 2025
stack-info: PR: #4062, branch: GarrettBeatty/stacked/4
@GarrettBeatty GarrettBeatty changed the base branch from gcbeatty/tuuploadresponse to GarrettBeatty/stacked/3 October 19, 2025 18:15
@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/3 to gcbeatty/tuuploadresponse October 20, 2025 13:38
GarrettBeatty added a commit that referenced this pull request Oct 20, 2025
stack-info: PR: #4062, branch: GarrettBeatty/stacked/4
@GarrettBeatty GarrettBeatty force-pushed the GarrettBeatty/stacked/4 branch from 8d603df to 622b802 Compare October 20, 2025 13:38
@GarrettBeatty GarrettBeatty changed the base branch from gcbeatty/tuuploadresponse to GarrettBeatty/stacked/3 October 20, 2025 13:38
@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/3 to feature/transfermanager October 20, 2025 14:06
GarrettBeatty added a commit that referenced this pull request Oct 20, 2025
stack-info: PR: #4062, branch: GarrettBeatty/stacked/4
@GarrettBeatty GarrettBeatty force-pushed the GarrettBeatty/stacked/4 branch from 622b802 to bb1413b Compare October 20, 2025 14:06
@GarrettBeatty GarrettBeatty changed the base branch from feature/transfermanager to GarrettBeatty/stacked/3 October 20, 2025 14:06
@GarrettBeatty
Copy link
Contributor Author

GarrettBeatty commented Oct 20, 2025

@philasmar this pr is still in draft but wanted to get your feedback on this approach. seems to be the easiest/best way to add this new api and re-use the existing functionality but using generics. i havent filled in the details of each response object yet (they are placeholder classes). was going to do that after

/// <typeparam name="TResponse">Type of response returned by the command</typeparam>
internal abstract partial class BaseCommand<TResponse> where TResponse : class
{
public virtual object Return
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not being used anywhere so removing it to avoid confusion

@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/9 to feature/transfermanager October 24, 2025 13:48
@GarrettBeatty GarrettBeatty force-pushed the GarrettBeatty/stacked/4 branch from a3dfaec to d3860eb Compare October 24, 2025 13:48
@GarrettBeatty GarrettBeatty changed the base branch from feature/transfermanager to GarrettBeatty/stacked/9 October 24, 2025 13:49
};
InternalSDKUtils.ApplyValuesV2(request, additionalProperties);
return transfer.UploadAsync(request, cancellationToken);
return UploadObjectFromStreamInternalAsync(bucketName, objectKey, stream, additionalProperties, cancellationToken);
Copy link
Contributor Author

@GarrettBeatty GarrettBeatty Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made a helper function here so that we could use the new uploadwithresponseasync api but then still have the same function signature of returning Task only (not Task<TransferUtilityUploadResponse>

await WhenAllOrFirstExceptionAsync(pendingTasks,cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false);

return new TransferUtilityAbortMultipartUploadsResponse();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont think we plan on populating this at all FYI

@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/9 to feature/transfermanager October 24, 2025 14:57
@GarrettBeatty GarrettBeatty force-pushed the GarrettBeatty/stacked/4 branch from d3860eb to 6d18ba4 Compare October 24, 2025 14:57
@GarrettBeatty GarrettBeatty changed the base branch from feature/transfermanager to GarrettBeatty/stacked/9 October 24, 2025 14:57
@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/9 to feature/transfermanager October 24, 2025 15:17
@GarrettBeatty GarrettBeatty changed the base branch from feature/transfermanager to GarrettBeatty/stacked/9 October 24, 2025 15:18
@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/9 to feature/transfermanager October 24, 2025 15:46
@GarrettBeatty GarrettBeatty changed the base branch from feature/transfermanager to GarrettBeatty/stacked/9 October 24, 2025 15:46
@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/9 to feature/transfermanager October 24, 2025 16:19
@GarrettBeatty GarrettBeatty changed the base branch from feature/transfermanager to GarrettBeatty/stacked/9 October 24, 2025 16:19
@GarrettBeatty GarrettBeatty marked this pull request as ready for review October 24, 2025 16:38
try
{
UploadAsync(filePath, bucketName).Wait();
UploadWithResponseAsync(filePath, bucketName).Wait();
Copy link
Contributor Author

@GarrettBeatty GarrettBeatty Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually i just realized this should say as UploadAsync because otherwise this would eventually create a span with UploadAsyncWithResponse instead of UploadAsync. will update that in next revision. same in other places below

@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/9 to feature/transfermanager October 24, 2025 22:24
@GarrettBeatty GarrettBeatty force-pushed the GarrettBeatty/stacked/4 branch from 6d18ba4 to 73b483f Compare October 24, 2025 22:24
@GarrettBeatty GarrettBeatty changed the base branch from feature/transfermanager to GarrettBeatty/stacked/9 October 24, 2025 22:24
stack-info: PR: #4075, branch: GarrettBeatty/stacked/8
stack-info: PR: #4076, branch: GarrettBeatty/stacked/9
stack-info: PR: #4062, branch: GarrettBeatty/stacked/4

add obsolete functions
@GarrettBeatty GarrettBeatty changed the base branch from GarrettBeatty/stacked/9 to feature/transfermanager October 28, 2025 13:18
@GarrettBeatty GarrettBeatty force-pushed the GarrettBeatty/stacked/4 branch from 73b483f to 60fdeae Compare October 28, 2025 13:18
@GarrettBeatty GarrettBeatty changed the base branch from feature/transfermanager to GarrettBeatty/stacked/9 October 28, 2025 13:18
/// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
/// </param>
/// <returns>The task object representing the asynchronous operation.</returns>
[Obsolete("Use UploadWithResponseAsync instead which allows you to upload files and also returns response metadata.")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to mark these as obsolete?

/// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
/// </param>
/// <returns>The task object representing the asynchronous operation.</returns>
[Obsolete("Use UploadWithResponseAsync instead which allows you to upload files and also returns response metadata.")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same question. the obsolete reason is not really convincing enough to mark the method as obsolete.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we want to encourage users to use the new functions. we are also creating downloadwithresponse (Which will have multi part download) so having them use the "withResponse" functions of both upload/download makes sense to me

/// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
/// </param>
/// <returns>The task object representing the asynchronous operation.</returns>
[Obsolete("Use UploadWithResponseAsync instead which allows you to upload files and also returns response metadata.")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment

/// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
/// </param>
/// <returns>The task object representing the asynchronous operation.</returns>
[Obsolete("Use UploadWithResponseAsync instead which allows you to upload files and also returns response metadata.")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment

Copy link
Contributor

@peterrsongg peterrsongg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should remove the return null statements, everything else are just small nits

}

/// <summary>
/// Uploads the specified file and returns response metadata including ETag, encryption details, and version information.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: spacing in the comments


#region Internal Helper Methods

private async Task UploadObjectFromStreamInternalAsync(string bucketName, string objectKey, Stream stream, IDictionary<string, object> additionalProperties, CancellationToken cancellationToken)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we pass in TURequest object instead so the number of params goes down? best practice to not have too many params.

@GarrettBeatty
Copy link
Contributor Author

opened this pr #4085

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants