feat: add object tagging support

Implements object tagging capabilities (v0.14.0) including:
- Core APIs: PutObjectTagging, GetObjectTagging, DeleteObjectTagging
- Integration with FilePut, FileUpload, and CopyObject operations
- Helper functions for URL-encoded tag header formatting
- Comprehensive input validation (max 10 tags, required fields)

Addresses specific compatibility issues with MinIO and Cloudflare R2:
- Implements a 2-step workaround for CopyObject (copy first, then tag) to avoid SignatureDoesNotMatch errors caused by the x-amz-tagging-directive header on non-AWS providers.

Includes comprehensive integration tests and documentation updates.
This commit is contained in:
Rohan Verma
2025-11-27 16:14:32 +05:30
parent ba9ef916bc
commit 11cad3573f
6 changed files with 923 additions and 6 deletions
+28
View File
@@ -7,7 +7,9 @@ import (
"encoding/hex"
"fmt"
"io"
"net/url"
"regexp"
"sort"
"strings"
"unicode/utf8"
)
@@ -95,3 +97,29 @@ func encodePath(pathName string) string {
}
return encodedPathname.String()
}
// encodeTagsHeader encodes tags as a URL-encoded key=value string for the x-amz-tagging header.
// Format: key1=value1&key2=value2
// Keys are sorted for consistency.
func encodeTagsHeader(tags map[string]string) string {
if len(tags) == 0 {
return ""
}
// Sort keys for consistent output
keys := make([]string, 0, len(tags))
for k := range tags {
keys = append(keys, k)
}
sort.Strings(keys)
// Build encoded string
parts := make([]string, 0, len(tags))
for _, k := range keys {
encodedKey := url.QueryEscape(k)
encodedValue := url.QueryEscape(tags[k])
parts = append(parts, encodedKey+"="+encodedValue)
}
return strings.Join(parts, "&")
}