From 1f025608a88a019ba0bbfccfdd6b652de8e05a0c Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Sun, 24 Nov 2024 16:32:39 -0800 Subject: [PATCH] Add support for Content-Language --- fakestorage/object.go | 20 ++++++++++++++++++++ fakestorage/object_test.go | 5 +++++ fakestorage/response.go | 2 ++ fakestorage/upload.go | 2 ++ internal/backend/object.go | 1 + internal/grpc/server.go | 2 ++ 6 files changed, 32 insertions(+) diff --git a/fakestorage/object.go b/fakestorage/object.go index b229a45233..1b084f7d09 100644 --- a/fakestorage/object.go +++ b/fakestorage/object.go @@ -35,6 +35,7 @@ type ObjectAttrs struct { ContentType string ContentEncoding string ContentDisposition string + ContentLanguage string CacheControl string // Crc32c checksum of Content. calculated by server when it's upload methods are used. Crc32c string @@ -62,6 +63,7 @@ type jsonObject struct { ContentType string `json:"contentType"` ContentEncoding string `json:"contentEncoding"` ContentDisposition string `json:"contentDisposition"` + ContentLanguage string `json:"contentLanguage"` Crc32c string `json:"crc32c,omitempty"` Md5Hash string `json:"md5Hash,omitempty"` Etag string `json:"etag,omitempty"` @@ -82,6 +84,7 @@ func (o ObjectAttrs) MarshalJSON() ([]byte, error) { ContentType: o.ContentType, ContentEncoding: o.ContentEncoding, ContentDisposition: o.ContentDisposition, + ContentLanguage: o.ContentLanguage, Size: o.Size, Crc32c: o.Crc32c, Md5Hash: o.Md5Hash, @@ -111,6 +114,7 @@ func (o *ObjectAttrs) UnmarshalJSON(data []byte) error { o.ContentType = temp.ContentType o.ContentEncoding = temp.ContentEncoding o.ContentDisposition = temp.ContentDisposition + o.ContentLanguage = temp.ContentLanguage o.Size = temp.Size o.Crc32c = temp.Crc32c o.Md5Hash = temp.Md5Hash @@ -397,6 +401,7 @@ func toBackendObjects(objects []StreamingObject) []backend.StreamingObject { ContentType: o.ContentType, ContentEncoding: o.ContentEncoding, ContentDisposition: o.ContentDisposition, + ContentLanguage: o.ContentLanguage, CacheControl: o.CacheControl, ACL: o.ACL, Created: getCurrentIfZero(o.Created).Format(timestampFormat), @@ -423,6 +428,7 @@ func bufferedObjectsToBackendObjects(objects []Object) []backend.StreamingObject ContentType: o.ContentType, ContentEncoding: o.ContentEncoding, ContentDisposition: o.ContentDisposition, + ContentLanguage: o.ContentLanguage, ACL: o.ACL, Created: getCurrentIfZero(o.Created).Format(timestampFormat), Deleted: o.Deleted.Format(timestampFormat), @@ -452,6 +458,7 @@ func fromBackendObjects(objects []backend.StreamingObject) []StreamingObject { ContentType: o.ContentType, ContentEncoding: o.ContentEncoding, ContentDisposition: o.ContentDisposition, + ContentLanguage: o.ContentLanguage, CacheControl: o.CacheControl, Crc32c: o.Crc32c, Md5Hash: o.Md5Hash, @@ -480,6 +487,7 @@ func fromBackendObjectsAttrs(objectAttrs []backend.ObjectAttrs) []ObjectAttrs { ContentType: o.ContentType, ContentEncoding: o.ContentEncoding, ContentDisposition: o.ContentDisposition, + ContentLanguage: o.ContentLanguage, CacheControl: o.CacheControl, Crc32c: o.Crc32c, Md5Hash: o.Md5Hash, @@ -783,6 +791,9 @@ func (s *Server) rewriteObject(r *http.Request) jsonResponse { if metadata.ContentDisposition == "" { metadata.ContentDisposition = obj.ContentDisposition } + if metadata.ContentLanguage == "" { + metadata.ContentLanguage = obj.ContentLanguage + } dstBucket := vars["destinationBucket"] newObject := StreamingObject{ @@ -793,6 +804,7 @@ func (s *Server) rewriteObject(r *http.Request) jsonResponse { ContentType: metadata.ContentType, ContentEncoding: metadata.ContentEncoding, ContentDisposition: metadata.ContentDisposition, + ContentLanguage: metadata.ContentLanguage, Metadata: metadata.Metadata, }, Content: obj.Content, @@ -912,6 +924,9 @@ func (s *Server) downloadObject(w http.ResponseWriter, r *http.Request) { if obj.ContentDisposition != "" { w.Header().Set("Content-Disposition", obj.ContentDisposition) } + if obj.ContentLanguage != "" { + w.Header().Set("Content-Language", obj.ContentLanguage) + } // X-Goog-Stored-Content-Encoding must be set to the original encoding, // defaulting to "identity" if no encoding was set. storedContentEncoding := "identity" @@ -1037,6 +1052,7 @@ func (s *Server) patchObject(r *http.Request) jsonResponse { ContentType string ContentEncoding string ContentDisposition string + ContentLanguage string Metadata map[string]string `json:"metadata"` CustomTime string Acl []acls @@ -1054,6 +1070,7 @@ func (s *Server) patchObject(r *http.Request) jsonResponse { attrsToUpdate.ContentType = payload.ContentType attrsToUpdate.ContentEncoding = payload.ContentEncoding attrsToUpdate.ContentDisposition = payload.ContentDisposition + attrsToUpdate.ContentLanguage = payload.ContentLanguage attrsToUpdate.Metadata = payload.Metadata attrsToUpdate.CustomTime = payload.CustomTime @@ -1092,6 +1109,7 @@ func (s *Server) updateObject(r *http.Request) jsonResponse { Metadata map[string]string `json:"metadata"` ContentType string `json:"contentType"` ContentDisposition string `json:"contentDisposition"` + ContentLanguage string `json:"contentLanguage"` CustomTime string Acl []acls } @@ -1109,6 +1127,7 @@ func (s *Server) updateObject(r *http.Request) jsonResponse { attrsToUpdate.CustomTime = payload.CustomTime attrsToUpdate.ContentType = payload.ContentType attrsToUpdate.ContentDisposition = payload.ContentDisposition + attrsToUpdate.ContentLanguage = payload.ContentLanguage if len(payload.Acl) > 0 { attrsToUpdate.ACL = []storage.ACLRule{} for _, aclData := range payload.Acl { @@ -1142,6 +1161,7 @@ func (s *Server) composeObject(r *http.Request) jsonResponse { Bucket string ContentType string ContentDisposition string + ContentLanguage string Metadata map[string]string } } diff --git a/fakestorage/object_test.go b/fakestorage/object_test.go index 2fb33d02da..3b58a094cb 100644 --- a/fakestorage/object_test.go +++ b/fakestorage/object_test.go @@ -51,6 +51,7 @@ func getObjectTestCases() objectTestCases { contentType = "text/plain; charset=utf-8" contentEncoding = "gzip" contentDisposition = "attachment; filename=\"replaced.txt\"" + contentLanguage = "fr" metaValue = "MetaValue" ) testInitExecTime := time.Now().Truncate(time.Microsecond) @@ -113,6 +114,7 @@ func getObjectTestCases() objectTestCases { ContentType: contentType, ContentEncoding: contentEncoding, ContentDisposition: contentDisposition, + ContentLanguage: contentLanguage, Crc32c: checksum.EncodedChecksum(uint32ToBytes(u32Checksum)), Md5Hash: checksum.EncodedHash(hash), Metadata: map[string]string{"MetaHeader": metaValue}, @@ -175,6 +177,9 @@ func checkObjectAttrs(testObj Object, attrs *storage.ObjectAttrs, t *testing.T) if attrs.ContentDisposition != testObj.ContentDisposition { t.Errorf("wrong content disposition\nwant %q\ngot %q", testObj.ContentDisposition, attrs.ContentDisposition) } + if attrs.ContentLanguage != testObj.ContentLanguage { + t.Errorf("wrong content language\nwant %q\ngot %q", testObj.ContentLanguage, attrs.ContentLanguage) + } if testObj.Content != nil && attrs.Size != int64(len(testObj.Content)) { t.Errorf("wrong size returned\nwant %d\ngot %d", len(testObj.Content), attrs.Size) } diff --git a/fakestorage/response.go b/fakestorage/response.go index f40dcd3fe9..6e06849195 100644 --- a/fakestorage/response.go +++ b/fakestorage/response.go @@ -119,6 +119,7 @@ type objectResponse struct { ContentType string `json:"contentType,omitempty"` ContentEncoding string `json:"contentEncoding,omitempty"` ContentDisposition string `json:"contentDisposition,omitempty"` + ContentLanguage string `json:"contentLanguage,omitempty"` Crc32c string `json:"crc32c,omitempty"` ACL []*objectAccessControl `json:"acl,omitempty"` Md5Hash string `json:"md5Hash,omitempty"` @@ -156,6 +157,7 @@ func newObjectResponse(obj ObjectAttrs, externalURL string) objectResponse { ContentType: obj.ContentType, ContentEncoding: obj.ContentEncoding, ContentDisposition: obj.ContentDisposition, + ContentLanguage: obj.ContentLanguage, Crc32c: obj.Crc32c, Md5Hash: obj.Md5Hash, Etag: obj.Etag, diff --git a/fakestorage/upload.go b/fakestorage/upload.go index e9181df46f..91899f9769 100644 --- a/fakestorage/upload.go +++ b/fakestorage/upload.go @@ -54,6 +54,7 @@ type multipartMetadata struct { ContentType string `json:"contentType"` ContentEncoding string `json:"contentEncoding"` ContentDisposition string `json:"contentDisposition"` + ContentLanguage string `json:"ContentLanguage"` CacheControl string `json:"cacheControl"` CustomTime time.Time `json:"customTime,omitempty"` Name string `json:"name"` @@ -382,6 +383,7 @@ func (s *Server) multipartUpload(bucketName string, r *http.Request) jsonRespons CacheControl: metadata.CacheControl, ContentEncoding: metadata.ContentEncoding, ContentDisposition: metadata.ContentDisposition, + ContentLanguage: metadata.ContentLanguage, CustomTime: metadata.CustomTime, ACL: getObjectACL(predefinedACL), Metadata: metadata.Metadata, diff --git a/internal/backend/object.go b/internal/backend/object.go index 63bf8d6d14..02673be568 100644 --- a/internal/backend/object.go +++ b/internal/backend/object.go @@ -21,6 +21,7 @@ type ObjectAttrs struct { ContentType string ContentEncoding string ContentDisposition string + ContentLanguage string CacheControl string Crc32c string Md5Hash string diff --git a/internal/grpc/server.go b/internal/grpc/server.go index 6538666bc7..fe1df99115 100644 --- a/internal/grpc/server.go +++ b/internal/grpc/server.go @@ -114,6 +114,7 @@ func (g *Server) GetObject(ctx context.Context, req *pb.GetObjectRequest) (*pb.O Generation: obj.ObjectAttrs.Generation, ContentType: obj.ObjectAttrs.ContentType, ContentDisposition: obj.ObjectAttrs.ContentDisposition, + ContentLanguage: obj.ObjectAttrs.ContentLanguage, }, nil } @@ -136,6 +137,7 @@ func (g *Server) PatchObject(ctx context.Context, req *pb.PatchObjectRequest) (* ContentType: req.Metadata.ContentType, ContentEncoding: req.Metadata.ContentEncoding, ContentDisposition: req.Metadata.ContentDisposition, + ContentLanguage: req.Metadata.ContentLanguage, } obj, err := g.backend.PatchObject(req.Bucket, req.Object, attrs) return makeObject(obj), err