Repository: aws/aws-sdk-go (go)

Issue:
Returning ETags for s3manager.Uploader
### Is this related to a problem?

[s3manager's Uploader.Upload](https://godoc.org/github.com/aws/aws-sdk-go/service/s3/s3manager#Uploader.Upload) is a super convenient facade - it's nice that it abstracts single vs multipart uploads and provides knobs for concurrency.

However, it seems like it doesn't return ETag information. That would be useful for doing subsequent operations on the key using S3's condition matching features.

### Feature description

Add an `ETag string` field to [UploadOutput](https://godoc.org/github.com/aws/aws-sdk-go/service/s3/s3manager#UploadOutput).

Populate it from the [CompleteMultipartUpload](https://github.com/aws/aws-sdk-go/blob/1f5f7a448b2ef1e5ef20489682e0a115b32208a9/service/s3/s3manager/upload.go#L595) response or the [PutObject](https://github.com/aws/aws-sdk-go/blob/1f5f7a448b2ef1e5ef20489682e0a115b32208a9/service/s3/s3manager/upload.go#L492) response as appropriate.

I think this is a fairly straightforward change, but since I'm a go newbie, I figured I'd raise an issue before opening a PR in case I'm missing something obvious.

### Describe alternatives you've considered

I've considered having my client app make an extra call to fetch the ETag after the fact (bad for latency, incurs additional request costs, has eventual consistency issues in the case of overwriting an existing key) or computing the ETag on the client side (fiddly, relies on implementation details)

Pull request:
service/s3/s3manager: Add ETag field to UploadOutput
Fixes #2764

PR already exists(#2809) . But it's not active. So I created another PR.

Patch:
diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md
index f32f60a69..50bbf6586 100644
--- a/CHANGELOG_PENDING.md
+++ b/CHANGELOG_PENDING.md
@@ -1,6 +1,9 @@
 ### SDK Features
 
 ### SDK Enhancements
+* `service/s3/s3manager`: Add ETag field to UploadOutput ([#3733](https://github.com/aws/aws-sdk-go/pull/3733))
+  * Adds the ETag field to the Uploader's UploadOutput return value.
+  * Fixes [#2764](https://github.com/aws/aws-sdk-go/issues/2764)
 * `aws`: Add `WithLowerCaseHeaderMaps` and `WithDisableRestProtocolURICleaning` to `aws.Config`. ([#3671](https://github.com/aws/aws-sdk-go/pull/3671))
 
 ### SDK Bugs
diff --git a/service/s3/s3manager/upload.go b/service/s3/s3manager/upload.go
index 8770d4041..7dba8347b 100644
--- a/service/s3/s3manager/upload.go
+++ b/service/s3/s3manager/upload.go
@@ -109,6 +109,9 @@ type UploadOutput struct {
 	// The ID for a multipart upload to S3. In the case of an error the error
 	// can be cast to the MultiUploadFailure interface to extract the upload ID.
 	UploadID string
+
+	// Entity tag of the object.
+	ETag *string
 }
 
 // WithUploaderRequestOptions appends to the Uploader's API request options.
@@ -527,6 +530,7 @@ func (u *uploader) singlePart(r io.ReadSeeker, cleanup func()) (*UploadOutput, e
 	return &UploadOutput{
 		Location:  url,
 		VersionID: out.VersionId,
+		ETag:      out.ETag,
 	}, nil
 }
 
@@ -632,6 +636,7 @@ func (u *multiuploader) upload(firstBuf io.ReadSeeker, cleanup func()) (*UploadO
 		Location:  uploadLocation,
 		VersionID: complete.VersionId,
 		UploadID:  u.uploadID,
+		ETag:      complete.ETag,
 	}, nil
 }
 


Test patch:
diff --git a/service/s3/s3manager/upload_test.go b/service/s3/s3manager/upload_test.go
index 618e25a2c..7986a5950 100644
--- a/service/s3/s3manager/upload_test.go
+++ b/service/s3/s3manager/upload_test.go
@@ -99,8 +99,10 @@ func loggingSvc(ignoreOps []string) (*s3.S3, *[]string, *[]interface{}) {
 		case *s3.CompleteMultipartUploadOutput:
 			data.Location = aws.String("https://location")
 			data.VersionId = aws.String("VERSION-ID")
+			data.ETag = aws.String("ETAG")
 		case *s3.PutObjectOutput:
 			data.VersionId = aws.String("VERSION-ID")
+			data.ETag = aws.String("ETAG")
 		}
 	})
 
@@ -147,6 +149,10 @@ func TestUploadOrderMulti(t *testing.T) {
 		t.Errorf("Expected %q, but received %q", "VERSION-ID", *resp.VersionID)
 	}
 
+	if "ETAG" != *resp.ETag {
+		t.Errorf("Expected %q, but received %q", "ETAG", *resp.ETag)
+	}
+
 	// Validate input values
 
 	// UploadPart
@@ -320,6 +326,10 @@ func TestUploadOrderSingle(t *testing.T) {
 		t.Errorf("Expected %q, but received %q", e, *resp.VersionID)
 	}
 
+	if "ETAG" != *resp.ETag {
+		t.Errorf("Expected %q, but received %q", "ETAG", *resp.ETag)
+	}
+
 	if len(resp.UploadID) > 0 {
 		t.Errorf("Expected empty string, but received %q", resp.UploadID)
 	}