diff --git a/api/handler.go b/api/handler.go index 265f572..9a061b7 100644 --- a/api/handler.go +++ b/api/handler.go @@ -48,6 +48,7 @@ func Handler() *mux.Router { m.Get(router.DeleteObservationType).Handler(handler(serveDeleteObservationType)) m.Get(router.Observation).Handler(handler(serveObservation)) + m.Get(router.CreateObservation).Handler(handler(serveCreateObservation)) return m } diff --git a/api/observations.go b/api/observations.go index f902664..4a1827e 100644 --- a/api/observations.go +++ b/api/observations.go @@ -1,10 +1,12 @@ package api import ( + "encoding/json" "net/http" "strconv" "github.com/gorilla/mux" + "github.com/thermokarst/bactdb/models" ) func serveObservation(w http.ResponseWriter, r *http.Request) error { @@ -20,3 +22,21 @@ func serveObservation(w http.ResponseWriter, r *http.Request) error { return writeJSON(w, observation) } + +func serveCreateObservation(w http.ResponseWriter, r *http.Request) error { + var observation models.Observation + err := json.NewDecoder(r.Body).Decode(&observation) + if err != nil { + return err + } + + created, err := store.Observations.Create(&observation) + if err != nil { + return err + } + if created { + w.WriteHeader(http.StatusCreated) + } + + return writeJSON(w, observation) +} diff --git a/api/observations_test.go b/api/observations_test.go index 26c8cd4..e39b24b 100644 --- a/api/observations_test.go +++ b/api/observations_test.go @@ -38,3 +38,30 @@ func TestObservation_Get(t *testing.T) { t.Errorf("got %+v but wanted %+v", got, want) } } + +func TestObservation_Create(t *testing.T) { + setup() + + want := newObservation() + + calledPost := false + store.Observations.(*models.MockObservationsService).Create_ = func(observation *models.Observation) (bool, error) { + if !normalizeDeepEqual(want, observation) { + t.Errorf("wanted request for observation %d but got %d", want, observation) + } + calledPost = true + return true, nil + } + + success, err := apiClient.Observations.Create(want) + if err != nil { + t.Fatal(err) + } + + if !calledPost { + t.Error("!calledPost") + } + if !success { + t.Error("!success") + } +} diff --git a/datastore/observations.go b/datastore/observations.go index e9090b0..c6fe565 100644 --- a/datastore/observations.go +++ b/datastore/observations.go @@ -1,6 +1,10 @@ package datastore -import "github.com/thermokarst/bactdb/models" +import ( + "time" + + "github.com/thermokarst/bactdb/models" +) func init() { DB.AddTableWithName(models.Observation{}, "observations").SetKeys(true, "Id") @@ -20,3 +24,13 @@ func (s *observationsStore) Get(id int64) (*models.Observation, error) { } return observation[0], nil } + +func (s *observationsStore) Create(observation *models.Observation) (bool, error) { + currentTime := time.Now() + observation.CreatedAt = currentTime + observation.UpdatedAt = currentTime + if err := s.dbh.Insert(observation); err != nil { + return false, err + } + return true, nil +} diff --git a/datastore/observations_test.go b/datastore/observations_test.go index eae9ee3..113f709 100644 --- a/datastore/observations_test.go +++ b/datastore/observations_test.go @@ -45,3 +45,23 @@ func TestObservationsStore_Get_db(t *testing.T) { t.Errorf("got observation %+v, want %+v", observation, want) } } + +func TestObservationsStore_Create_db(t *testing.T) { + tx, _ := DB.Begin() + defer tx.Rollback() + + observation := newObservation(t, tx) + + d := NewDatastore(tx) + + created, err := d.Observations.Create(observation) + if err != nil { + t.Fatal(err) + } + if !created { + t.Error("!created") + } + if observation.Id == 0 { + t.Error("want nonzero observation.Id after submitting") + } +} diff --git a/models/observations.go b/models/observations.go index 95971ad..824a4c1 100644 --- a/models/observations.go +++ b/models/observations.go @@ -2,6 +2,7 @@ package models import ( "errors" + "net/http" "strconv" "time" @@ -28,6 +29,9 @@ func NewObservation() *Observation { type ObservationsService interface { // Get an observation Get(id int64) (*Observation, error) + + // Create an observation + Create(observation *Observation) (bool, error) } var ( @@ -60,8 +64,28 @@ func (s *observationsService) Get(id int64) (*Observation, error) { return observation, nil } +func (s *observationsService) Create(observation *Observation) (bool, error) { + url, err := s.client.url(router.CreateObservation, nil, nil) + if err != nil { + return false, err + } + + req, err := s.client.NewRequest("POST", url.String(), observation) + if err != nil { + return false, err + } + + resp, err := s.client.Do(req, &observation) + if err != nil { + return false, err + } + + return resp.StatusCode == http.StatusCreated, nil +} + type MockObservationsService struct { - Get_ func(id int64) (*Observation, error) + Get_ func(id int64) (*Observation, error) + Create_ func(observation *Observation) (bool, error) } var _ObservationsService = &MockObservationsService{} @@ -72,3 +96,10 @@ func (s *MockObservationsService) Get(id int64) (*Observation, error) { } return s.Get_(id) } + +func (s *MockObservationsService) Create(observation *Observation) (bool, error) { + if s.Create_ == nil { + return false, nil + } + return s.Create_(observation) +} diff --git a/models/observations_test.go b/models/observations_test.go index d3069ea..2b3d595 100644 --- a/models/observations_test.go +++ b/models/observations_test.go @@ -43,3 +43,39 @@ func TestObservationService_Get(t *testing.T) { t.Errorf("Observations.Get return %+v, want %+v", observation, want) } } + +func TestObservationService_Create(t *testing.T) { + setup() + defer teardown() + + want := newObservation() + + var called bool + mux.HandleFunc(urlPath(t, router.CreateObservation, nil), func(w http.ResponseWriter, r *http.Request) { + called = true + testMethod(t, r, "POST") + testBody(t, r, `{"id":1,"observation_name":"Test Observation","observation_type_id":0,"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","deleted_at":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + + w.WriteHeader(http.StatusCreated) + writeJSON(w, want) + }) + + observation := newObservation() + created, err := client.Observations.Create(observation) + if err != nil { + t.Errorf("Observations.Create returned error: %v", err) + } + + if !created { + t.Error("!created") + } + + if !called { + t.Fatal("!called") + } + + normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt) + if !reflect.DeepEqual(observation, want) { + t.Errorf("Observations.Create returned %+v, want %+v", observation, want) + } +} diff --git a/router/api.go b/router/api.go index a5f8ddb..046c8be 100644 --- a/router/api.go +++ b/router/api.go @@ -39,6 +39,7 @@ func API() *mux.Router { m.Path("/observation_types/{Id:.+}").Methods("DELETE").Name(DeleteObservationType) // Observations + m.Path("/observations").Methods("POST").Name(CreateObservation) m.Path("/observations/{Id:.+}").Methods("GET").Name(Observation) return m diff --git a/router/routes.go b/router/routes.go index 5d4e6eb..0b57436 100644 --- a/router/routes.go +++ b/router/routes.go @@ -29,5 +29,6 @@ const ( UpdateObservationType = "observation_type:update" DeleteObservationType = "observation_type:delete" - Observation = "observation:get" + Observation = "observation:get" + CreateObservation = "observation:create" )