validate db and frame

This commit is contained in:
Linh Vo 2017-04-02 22:26:36 -05:00
parent 261e327014
commit c034293967
3 changed files with 173 additions and 4 deletions

2
db.go
View file

@ -478,7 +478,7 @@ func MergeSchemas(a, b []*DBInfo) []*DBInfo {
// DBOptions represents options to set when initializing a db.
type DBOptions struct {
ColumnLabel string `json:"columnLabel,omitempty"`
ColumnLabel string `json:"columnLabel,omitempty" valid:"required,string"`
}
// hasTime returns true if a contains a non-nil time.

View file

@ -17,6 +17,7 @@ import (
"strings"
"time"
"bytes"
"github.com/gogo/protobuf/proto"
"github.com/pilosa/pilosa/internal"
"github.com/pilosa/pilosa/pql"
@ -321,6 +322,16 @@ type sliceMaxResponse struct {
// handlePostDB handles POST /db request.
func (h *Handler) handlePostDB(w http.ResponseWriter, r *http.Request) {
var err error
// Copy request body for validation
buf, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
rdr2 := ioutil.NopCloser(bytes.NewBuffer(buf))
r.Body = rdr2
// Decode request.
var req postDBRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@ -328,8 +339,15 @@ func (h *Handler) handlePostDB(w http.ResponseWriter, r *http.Request) {
return
}
// Validate request
err = h.validateRequest(buf, r.URL.Path)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Create database.
_, err := h.Index.CreateDB(req.DB, req.Options)
_, err = h.Index.CreateDB(req.DB, req.Options)
if err == ErrDatabaseExists {
http.Error(w, err.Error(), http.StatusConflict)
return
@ -344,9 +362,57 @@ func (h *Handler) handlePostDB(w http.ResponseWriter, r *http.Request) {
}
}
// Validate request body for db/frame creation
func (h *Handler) validateRequest(r []byte, path string) error {
var data map[string]interface{}
err := json.Unmarshal(r, &data)
if err != nil {
return err
}
option, ok := data["options"]
if len(data) >= 2 && !ok {
return errors.New("options needs to be provided")
} else if ok {
err = h.validateOptions(path, option.(map[string]interface{}))
if err != nil {
return fmt.Errorf("invalid options: %s", option.(map[string]interface{}))
}
}
return nil
}
// Validate options in request body for db/frame creation, make sure key and value for columnLabel/rowLable is correct
func (h Handler) validateOptions(path string, options map[string]interface{}) error {
switch path {
case "/db":
if _, ok := options["columnLabel"]; !ok && len(options) > 0 {
return errors.New("columnLabel is not provided")
} else if ok {
err := ValidateName(options["columnLabel"].(string))
if err != nil {
return err
}
}
case "/frame":
if _, ok := options["rowLabel"]; !ok && len(options) > 0 {
return errors.New("rowLabel is not provided")
} else if ok {
err := ValidateName(options["rowLabel"].(string))
if err != nil {
return err
}
}
}
return nil
}
type postDBRequest struct {
DB string `json:"db"`
Options DBOptions `json:"options"`
Options DBOptions `json:"options" valid:"json"`
}
type postDBResponse struct{}
@ -478,6 +544,23 @@ type postDBAttrDiffResponse struct {
// handlePostFrame handles POST /frame request.
func (h *Handler) handlePostFrame(w http.ResponseWriter, r *http.Request) {
var err error
// Copy request body for validation
buf, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
rdr2 := ioutil.NopCloser(bytes.NewBuffer(buf))
r.Body = rdr2
// Validate request
err = h.validateRequest(buf, r.URL.Path)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Decode request.
var req postFrameRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@ -493,7 +576,7 @@ func (h *Handler) handlePostFrame(w http.ResponseWriter, r *http.Request) {
}
// Create frame.
_, err := db.CreateFrame(req.Frame, req.Options)
_, err = db.CreateFrame(req.Frame, req.Options)
if err == ErrFrameExists {
http.Error(w, err.Error(), http.StatusConflict)
return

View file

@ -14,6 +14,7 @@ import (
"strings"
"testing"
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/pilosa/pilosa"
"github.com/pilosa/pilosa/internal"
@ -868,3 +869,88 @@ func MustReadAll(r io.Reader) []byte {
}
return buf
}
// Ensure that options needs to be provided to set columnLabel when create DB
func TestHandler_DB_Options(t *testing.T) {
idx := MustOpenIndex()
defer idx.Close()
s := NewServer()
s.Handler.Index = idx.Index
defer s.Close()
resp, err := http.DefaultClient.Do(MustNewHTTPRequest("POST", s.URL+"/db", strings.NewReader(`{"db": "sample-db", "columnLabel": "location"}`)))
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
// Verify body response.
if resp.StatusCode != http.StatusBadRequest {
t.Fatalf("unexpected status: %d", resp.StatusCode)
} else if buf, err := ioutil.ReadAll(resp.Body); err != nil {
t.Fatal(err)
} else if string(buf) != "options needs to be provided"+"\n" {
fmt.Println(string(buf) == "options needs to be provided")
t.Fatalf("unexpected response body: %s", buf)
}
}
// Ensure that rowLabel is provided as an options when create frame
func TestHandler_Frame_Options(t *testing.T) {
idx := MustOpenIndex()
defer idx.Close()
s := NewServer()
s.Handler.Index = idx.Index
defer s.Close()
// Create database.
if _, err := idx.CreateDBIfNotExists("sample-db", pilosa.DBOptions{}); err != nil {
t.Fatal(err)
}
resp, err := http.DefaultClient.Do(MustNewHTTPRequest("POST", s.URL+"/frame", strings.NewReader(`{"db": "sample-db", "frame": "test", "options": {"columnLabel": "location"}}`)))
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
// Verify body response.
if resp.StatusCode != http.StatusBadRequest {
t.Fatalf("unexpected status: %d", resp.StatusCode)
} else if buf, err := ioutil.ReadAll(resp.Body); err != nil {
t.Fatal(err)
} else if string(buf) != "invalid options: map[columnLabel:location]"+"\n" {
t.Fatalf("unexpected response body: %s", buf)
}
}
// Ensure that rowLabel is provided as an options when create frame
func TestHandler_OptionsValue(t *testing.T) {
idx := MustOpenIndex()
defer idx.Close()
s := NewServer()
s.Handler.Index = idx.Index
defer s.Close()
// Create database.
if _, err := idx.CreateDBIfNotExists("sample-db", pilosa.DBOptions{}); err != nil {
t.Fatal(err)
}
resp, err := http.DefaultClient.Do(MustNewHTTPRequest("POST", s.URL+"/frame", strings.NewReader(`{"db": "sample-db", "options": {"rowLabel": "///"}}`)))
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
// Verify body response.
if resp.StatusCode != http.StatusBadRequest {
t.Fatalf("unexpected status: %d", resp.StatusCode)
} else if buf, err := ioutil.ReadAll(resp.Body); err != nil {
t.Fatal(err)
} else if string(buf) != "invalid options: map[rowLabel:///]"+"\n" {
t.Fatalf("unexpected response body: %s", buf)
}
}