Repository: fsouza/fake-gcs-server
Pull request #34 (2019-05-31 13:03:40)

Support creating a bucket with POST /storage/v1/b
This implements a handler for a ``POST`` to ``/storage/v1/b``, which creates a bucket by name.

I had a similar issue as reported in https://github.com/fsouza/fake-gcs-server/issues/24#issuecomment-477888602, that I expected to be able to use the client library to create buckets.


Patch:
diff --git a/fakestorage/bucket.go b/fakestorage/bucket.go
index e2fa2ad..4b325c6 100644
--- a/fakestorage/bucket.go
+++ b/fakestorage/bucket.go
@@ -24,6 +24,34 @@ func (s *Server) CreateBucket(name string) {
 	}
 }
 
+// createBucketByPost handles a POST request to create a bucket
+func (s *Server) createBucketByPost(w http.ResponseWriter, r *http.Request) {
+	// Minimal version of Bucket from google.golang.org/api/storage/v1
+	var data struct {
+		Name string
+	}
+
+	// Read the bucket name from the request body JSON
+	decoder := json.NewDecoder(r.Body)
+	if err := decoder.Decode(&data); err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	name := data.Name
+
+	// Create the named bucket
+	s.mtx.RLock()
+	defer s.mtx.RUnlock()
+	if err := s.backend.CreateBucket(name); err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	// Return the created bucket:
+	resp := newBucketResponse(name)
+	json.NewEncoder(w).Encode(resp)
+}
+
 func (s *Server) listBuckets(w http.ResponseWriter, r *http.Request) {
 	s.mtx.RLock()
 	defer s.mtx.RUnlock()
diff --git a/fakestorage/server.go b/fakestorage/server.go
index 165d9d7..bf9d971 100644
--- a/fakestorage/server.go
+++ b/fakestorage/server.go
@@ -130,6 +130,7 @@ func (s *Server) buildMuxer() {
 	s.mux.Host("{bucketName}.storage.googleapis.com").Path("/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
 	r := s.mux.PathPrefix("/storage/v1").Subrouter()
 	r.Path("/b").Methods("GET").HandlerFunc(s.listBuckets)
+	r.Path("/b").Methods("POST").HandlerFunc(s.createBucketByPost)
 	r.Path("/b/{bucketName}").Methods("GET").HandlerFunc(s.getBucket)
 	r.Path("/b/{bucketName}/o").Methods("GET").HandlerFunc(s.listObjects)
 	r.Path("/b/{bucketName}/o").Methods("POST").HandlerFunc(s.insertObject)


Test patch:
diff --git a/fakestorage/bucket_test.go b/fakestorage/bucket_test.go
index 2d2c808..e8f6439 100644
--- a/fakestorage/bucket_test.go
+++ b/fakestorage/bucket_test.go
@@ -50,6 +50,24 @@ func TestServerClientBucketAttrsAfterCreateBucket(t *testing.T) {
 	})
 }
 
+func TestServerClientBucketAttrsAfterCreateBucketByPost(t *testing.T) {
+	runServersTest(t, nil, func(t *testing.T, server *Server) {
+		const bucketName = "post-bucket"
+		client := server.Client()
+		bucket := client.Bucket(bucketName)
+		if err := bucket.Create(context.Background(), "whatever", nil); err != nil {
+			t.Fatal(err)
+		}
+		attrs, err := client.Bucket(bucketName).Attrs(context.Background())
+		if err != nil {
+			t.Fatal(err)
+		}
+		if attrs.Name != bucketName {
+			t.Errorf("wrong bucket name returned\nwant %q\ngot  %q", bucketName, attrs.Name)
+		}
+	})
+}
+
 func TestServerClientBucketAttrsNotFound(t *testing.T) {
 	runServersTest(t, nil, func(t *testing.T, server *Server) {
 		client := server.Client()