diff --git a/fakestorage/object.go b/fakestorage/object.go index e413f4c4ef..8dd40f6f0c 100644 --- a/fakestorage/object.go +++ b/fakestorage/object.go @@ -934,17 +934,37 @@ func (s *Server) patchObject(r *http.Request) jsonResponse { vars := unescapeMuxVars(mux.Vars(r)) bucketName := vars["bucketName"] objectName := vars["objectName"] - var metadata struct { + + type acls struct { + Entity string + Role string + } + + var payload struct { Metadata map[string]string `json:"metadata"` + Acl []acls } - err := json.NewDecoder(r.Body).Decode(&metadata) + err := json.NewDecoder(r.Body).Decode(&payload) if err != nil { return jsonResponse{ status: http.StatusBadRequest, errorMessage: "Metadata in the request couldn't decode", } } - backendObj, err := s.backend.PatchObject(bucketName, objectName, metadata.Metadata) + + var attrsToUpdate backend.ObjectAttrs + + attrsToUpdate.Metadata = payload.Metadata + + if len(payload.Acl) > 0 { + attrsToUpdate.ACL = []storage.ACLRule{} + for _, aclData := range payload.Acl { + newAcl := storage.ACLRule{Entity: storage.ACLEntity(aclData.Entity), Role: storage.ACLRole(aclData.Role)} + attrsToUpdate.ACL = append(attrsToUpdate.ACL, newAcl) + } + } + + backendObj, err := s.backend.PatchObject(bucketName, objectName, attrsToUpdate) if err != nil { return jsonResponse{ status: http.StatusNotFound, diff --git a/internal/backend/fs.go b/internal/backend/fs.go index 01f1ccef7d..5aa820fe58 100644 --- a/internal/backend/fs.go +++ b/internal/backend/fs.go @@ -13,6 +13,7 @@ import ( "net/url" "os" "path/filepath" + "reflect" "strings" "sync" "syscall" @@ -310,21 +311,34 @@ func (s *storageFS) DeleteObject(bucketName, objectName string) error { return os.Remove(path) } -// PatchObject patches the given object metadata. -func (s *storageFS) PatchObject(bucketName, objectName string, metadata map[string]string) (StreamingObject, error) { +// func (s *storageFS) PatchObject(bucketName, objectName string, metadata map[string]string) (StreamingObject, error) { +func (s *storageFS) PatchObject(bucketName, objectName string, attrsToUpdate ObjectAttrs) (StreamingObject, error) { obj, err := s.GetObject(bucketName, objectName) if err != nil { return StreamingObject{}, err } defer obj.Close() - if obj.Metadata == nil { - obj.Metadata = map[string]string{} - } - for k, v := range metadata { - obj.Metadata[k] = v + + currObjValues := reflect.ValueOf(&(obj.ObjectAttrs)).Elem() + currObjType := currObjValues.Type() + newObjValues := reflect.ValueOf(attrsToUpdate) + for i := 0; i < newObjValues.NumField(); i++ { + if reflect.Value.IsZero(newObjValues.Field(i)) { + continue + } else if currObjType.Field(i).Name == "Metadata" { + if obj.Metadata == nil { + obj.Metadata = map[string]string{} + } + for k, v := range attrsToUpdate.Metadata { + obj.Metadata[k] = v + } + } else { + currObjValues.Field(i).Set(newObjValues.Field(i)) + } } - obj.Generation = 0 // reset generation id - return s.CreateObject(obj, NoConditions{}) // recreate object + + obj.Generation = 0 // reset generation id + return s.CreateObject(obj, NoConditions{}) } // UpdateObject replaces the given object metadata. diff --git a/internal/backend/memory.go b/internal/backend/memory.go index 80e8bfee90..3aeeb8a007 100644 --- a/internal/backend/memory.go +++ b/internal/backend/memory.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "reflect" "strings" "sync" "time" @@ -300,18 +301,30 @@ func (s *storageMemory) DeleteObject(bucketName, objectName string) error { } // PatchObject updates an object metadata. -func (s *storageMemory) PatchObject(bucketName, objectName string, metadata map[string]string) (StreamingObject, error) { +func (s *storageMemory) PatchObject(bucketName, objectName string, attrsToUpdate ObjectAttrs) (StreamingObject, error) { obj, err := s.GetObject(bucketName, objectName) if err != nil { return StreamingObject{}, err } - if obj.Metadata == nil { - obj.Metadata = map[string]string{} - } - for k, v := range metadata { - obj.Metadata[k] = v + + currObjValues := reflect.ValueOf(&(obj.ObjectAttrs)).Elem() + currObjType := currObjValues.Type() + newObjValues := reflect.ValueOf(attrsToUpdate) + for i := 0; i < newObjValues.NumField(); i++ { + if reflect.Value.IsZero(newObjValues.Field(i)) { + continue + } else if currObjType.Field(i).Name == "Metadata" { + if obj.Metadata == nil { + obj.Metadata = map[string]string{} + } + for k, v := range attrsToUpdate.Metadata { + obj.Metadata[k] = v + } + } else { + currObjValues.Field(i).Set(newObjValues.Field(i)) + } } - s.CreateObject(obj, NoConditions{}) // recreate object + s.CreateObject(obj, NoConditions{}) return obj, nil } diff --git a/internal/backend/storage.go b/internal/backend/storage.go index 596b9cceae..756dd7f177 100644 --- a/internal/backend/storage.go +++ b/internal/backend/storage.go @@ -27,7 +27,7 @@ type Storage interface { GetObject(bucketName, objectName string) (StreamingObject, error) GetObjectWithGeneration(bucketName, objectName string, generation int64) (StreamingObject, error) DeleteObject(bucketName, objectName string) error - PatchObject(bucketName, objectName string, metadata map[string]string) (StreamingObject, error) + PatchObject(bucketName, objectName string, attrsToUpdate ObjectAttrs) (StreamingObject, error) UpdateObject(bucketName, objectName string, metadata map[string]string) (StreamingObject, error) ComposeObject(bucketName string, objectNames []string, destinationName string, metadata map[string]string, contentType string) (StreamingObject, error) } diff --git a/main.go b/main.go index db69daa907..b967d8a9c7 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "path/filepath" "syscall" + "cloud.google.com/go/storage" "github.com/fsouza/fake-gcs-server/fakestorage" "github.com/fsouza/fake-gcs-server/internal/checksum" "github.com/fsouza/fake-gcs-server/internal/config" @@ -103,6 +104,12 @@ func objectsFromBucket(localBucketPath, bucketName string) ([]fakestorage.Object } objects = append(objects, fakestorage.Object{ ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: bucketName, Name: objectKey, ContentType: mime.TypeByExtension(filepath.Ext(path)), diff --git a/main_test.go b/main_test.go index 9592131e8e..c9e0ed783d 100644 --- a/main_test.go +++ b/main_test.go @@ -11,6 +11,7 @@ import ( "os" "testing" + "cloud.google.com/go/storage" "github.com/fsouza/fake-gcs-server/fakestorage" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -48,6 +49,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { expectedObjects: []fakestorage.Object{ { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "sample-bucket", Name: "some_file.txt", ContentType: testContentType, @@ -63,6 +70,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { expectedObjects: []fakestorage.Object{ { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "some-bucket", Name: "a/b/c/d/e/f/object1.txt", ContentType: testContentType, @@ -71,6 +84,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { }, { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "some-bucket", Name: "a/b/c/d/e/f/object2.txt", ContentType: testContentType, @@ -79,6 +98,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { }, { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "some-bucket", Name: "root-object.txt", ContentType: testContentType, @@ -101,6 +126,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { expectedObjects: []fakestorage.Object{ { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "bucket1", Name: "object1.txt", ContentType: testContentType, @@ -109,6 +140,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { }, { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "bucket1", Name: "object2.txt", ContentType: testContentType, @@ -117,6 +154,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { }, { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "bucket2", Name: "object1.txt", ContentType: testContentType, @@ -125,6 +168,12 @@ func TestGenerateObjectsFromFiles(t *testing.T) { }, { ObjectAttrs: fakestorage.ObjectAttrs{ + ACL: []storage.ACLRule{ + { + Entity: "projectOwner-test-project", + Role: "OWNER", + }, + }, BucketName: "bucket2", Name: "object2.txt", ContentType: testContentType,