diff --git a/docs/api-reference.md b/docs/api-reference.md index 959c850d6..68dae46b5 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -241,7 +241,7 @@ curl -XDELETE localhost:10101/index/user/field/language Returns the schema of all indexes in JSON. ``` request -curl -XGET localhost:10101/index +curl -XGET localhost:10101/schema ``` ``` response { @@ -276,6 +276,24 @@ curl -XGET localhost:10101/index } ``` +### Duplicate schema into empty Pilosa cluster + +`POST /schema` + +To duplicate one Pilosa cluster's schema to another, it's possible to +pass the output of `GET /schema` as the request body of `POST /schema` +and all the indexes and fields in the schema will be created in +Pilosa. As of this writing, the behavior of POSTing a schema to a +non-empty Pilosa cluster is undefined. These semantics will likely be +ironed out in a future version. + +``` request +# after (e.g.) curl -XGET localhost:10101/schema > schema.json +curl -XPOST localhost:10101/schema --data-binary @schema.json +``` + +Response: `204 No Content` + ### Get version `GET /version` diff --git a/http/handler.go b/http/handler.go index 7d59c5b89..c6335e3dd 100644 --- a/http/handler.go +++ b/http/handler.go @@ -430,6 +430,7 @@ func (h *Handler) handlePostSchema(w http.ResponseWriter, r *http.Request) { http.Error(w, fmt.Sprintf("apply schema to Pilosa: %v", err), http.StatusBadRequest) return } + w.WriteHeader(http.StatusNoContent) } // handleGetStatus handles GET /status requests. diff --git a/server/handler_test.go b/server/handler_test.go index 082869081..1f56aa6f3 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -36,13 +36,13 @@ import ( "github.com/pilosa/pilosa/test" ) -// Ensure the handler returns "not found" for invalid paths. func TestHandler_Endpoints(t *testing.T) { cmd := test.MustRunCluster(t, 1)[0] h := cmd.Handler.(*http.Handler).Handler holder := cmd.Server.Holder() hldr := test.Holder{Holder: holder} + // Ensure the handler returns "not found" for invalid paths. t.Run("Not Found", func(t *testing.T) { w := httptest.NewRecorder() h.ServeHTTP(w, test.MustNewHTTPRequest("GET", "/no_such_path", nil)) @@ -51,6 +51,44 @@ func TestHandler_Endpoints(t *testing.T) { } }) + t.Run("SchemaEmpty", func(t *testing.T) { + w := httptest.NewRecorder() + h.ServeHTTP(w, test.MustNewHTTPRequest("GET", "/schema", nil)) + if w.Code != gohttp.StatusOK { + t.Fatalf("unexpected status code: %d", w.Code) + } + body := w.Body.String() + if body != "{\"indexes\":null}\n" { + t.Fatalf("unexpected empty schema: '%v'", body) + } + + }) + + t.Run("PostSchema", func(t *testing.T) { + w := httptest.NewRecorder() + h.ServeHTTP(w, test.MustNewHTTPRequest("POST", "/schema", strings.NewReader(`{"indexes":[{"name":"blah","options":{"keys":false,"trackExistence":true},"fields":[{"name":"f1","options":{"type":"set","cacheType":"ranked","cacheSize":50000,"keys":false}}],"shardWidth":1048576}]}`))) + if w.Code != gohttp.StatusNoContent { + t.Fatalf("unexpected code: %v", w.Code) + } + idx, err := cmd.API.Index(context.Background(), "blah") + if err != nil { + t.Fatalf("getting index: %v", err) + } + if idx.Name() != "blah" { + t.Fatalf("index did not get set, got %v", idx.Name()) + } + + fld, err := cmd.API.Field(context.Background(), "blah", "f1") + if err != nil { + t.Fatalf("getting field: %v", err) + } + if fld.Name() != "f1" { + t.Fatalf("unexpected field: %v", fld.Name()) + } + + h.ServeHTTP(w, test.MustNewHTTPRequest("DELETE", "/index/blah", nil)) + }) + t.Run("Info", func(t *testing.T) { w := httptest.NewRecorder() h.ServeHTTP(w, test.MustNewHTTPRequest("GET", "/info", nil))