diff --git a/api/handler.go b/api/handler.go index b2ed52a..2ffc70e 100644 --- a/api/handler.go +++ b/api/handler.go @@ -36,6 +36,7 @@ func Handler() *mux.Router { m.Get(router.DeleteSpecies).Handler(handler(serveDeleteSpecies)) m.Get(router.Strain).Handler(handler(serveStrain)) + m.Get(router.CreateStrain).Handler(handler(serveCreateStrain)) return m } diff --git a/api/strains.go b/api/strains.go index 13f3158..eb388e7 100644 --- a/api/strains.go +++ b/api/strains.go @@ -1,10 +1,12 @@ package api import ( + "encoding/json" "net/http" "strconv" "github.com/gorilla/mux" + "github.com/thermokarst/bactdb/models" ) func serveStrain(w http.ResponseWriter, r *http.Request) error { @@ -20,3 +22,21 @@ func serveStrain(w http.ResponseWriter, r *http.Request) error { return writeJSON(w, strain) } + +func serveCreateStrain(w http.ResponseWriter, r *http.Request) error { + var strain models.Strain + err := json.NewDecoder(r.Body).Decode(&strain) + if err != nil { + return err + } + + created, err := store.Strains.Create(&strain) + if err != nil { + return err + } + if created { + w.WriteHeader(http.StatusCreated) + } + + return writeJSON(w, strain) +} diff --git a/api/strains_test.go b/api/strains_test.go index eb90110..f0d234e 100644 --- a/api/strains_test.go +++ b/api/strains_test.go @@ -40,3 +40,30 @@ func TestStrain_Get(t *testing.T) { t.Errorf("got strain %+v but wanted strain %+v", got, want) } } + +func TestStrain_Create(t *testing.T) { + setup() + + want := newStrain() + + calledPost := false + store.Strains.(*models.MockStrainsService).Create_ = func(strain *models.Strain) (bool, error) { + if !normalizeDeepEqual(want, strain) { + t.Errorf("wanted request for strain %d but got %d", want, strain) + } + calledPost = true + return true, nil + } + + success, err := apiClient.Strains.Create(want) + if err != nil { + t.Fatal(err) + } + + if !calledPost { + t.Error("!calledPost") + } + if !success { + t.Error("!success") + } +} diff --git a/datastore/strains.go b/datastore/strains.go index 367fbb0..bd16d93 100644 --- a/datastore/strains.go +++ b/datastore/strains.go @@ -20,3 +20,10 @@ func (s *strainsStore) Get(id int64) (*models.Strain, error) { } return strain[0], nil } + +func (s *strainsStore) Create(strain *models.Strain) (bool, error) { + if err := s.dbh.Insert(strain); err != nil { + return false, err + } + return true, nil +} diff --git a/datastore/strains_test.go b/datastore/strains_test.go index 2b45c5a..cc35e31 100644 --- a/datastore/strains_test.go +++ b/datastore/strains_test.go @@ -45,3 +45,23 @@ func TestStrainsStore_Get_db(t *testing.T) { t.Errorf("got strain %+v, want %+v", strain, want) } } + +func TestStrainsStore_Create_db(t *testing.T) { + tx, _ := DB.Begin() + defer tx.Rollback() + + strain := newStrain(t, tx) + + d := NewDatastore(tx) + + created, err := d.Strains.Create(strain) + if err != nil { + t.Fatal(err) + } + if !created { + t.Error("!created") + } + if strain.Id == 0 { + t.Error("want nonzero strain.Id after submitting") + } +} diff --git a/models/strains.go b/models/strains.go index 3e5dd7c..d802f71 100644 --- a/models/strains.go +++ b/models/strains.go @@ -2,6 +2,7 @@ package models import ( "errors" + "net/http" "strconv" "time" @@ -30,6 +31,9 @@ func NewStrain() *Strain { type StrainsService interface { // Get a strain Get(id int64) (*Strain, error) + + // Create a strain record + Create(strain *Strain) (bool, error) } var ( @@ -62,8 +66,28 @@ func (s *strainsService) Get(id int64) (*Strain, error) { return strain, nil } +func (s *strainsService) Create(strain *Strain) (bool, error) { + url, err := s.client.url(router.CreateStrain, nil, nil) + if err != nil { + return false, err + } + + req, err := s.client.NewRequest("POST", url.String(), strain) + if err != nil { + return false, err + } + + resp, err := s.client.Do(req, &strain) + if err != nil { + return false, err + } + + return resp.StatusCode == http.StatusCreated, nil +} + type MockStrainsService struct { - Get_ func(id int64) (*Strain, error) + Get_ func(id int64) (*Strain, error) + Create_ func(strain *Strain) (bool, error) } var _ StrainsService = &MockStrainsService{} @@ -74,3 +98,10 @@ func (s *MockStrainsService) Get(id int64) (*Strain, error) { } return s.Get_(id) } + +func (s *MockStrainsService) Create(strain *Strain) (bool, error) { + if s.Create_ == nil { + return false, nil + } + return s.Create_(strain) +} diff --git a/models/strains_test.go b/models/strains_test.go index 0dd5275..797276a 100644 --- a/models/strains_test.go +++ b/models/strains_test.go @@ -44,3 +44,39 @@ func TestStrainService_Get(t *testing.T) { t.Errorf("Strain.Get return %+v, want %+v", strain, want) } } + +func TestStrainService_Create(t *testing.T) { + setup() + defer teardown() + + want := newStrain() + + var called bool + mux.HandleFunc(urlPath(t, router.CreateStrain, nil), func(w http.ResponseWriter, r *http.Request) { + called = true + testMethod(t, r, "POST") + testBody(t, r, `{"id":1,"species_id":1,"strain_name":"Test Strain","strain_type":"Test Type","etymology":"Test Etymology","accession_banks":"Test Accession","genbank_eml_ddb":"Test Genbank","created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","deleted_at":"0001-01-01T00:00:00Z"}`+"\n") + + w.WriteHeader(http.StatusCreated) + writeJSON(w, want) + }) + + strain := newStrain() + created, err := client.Strains.Create(strain) + if err != nil { + t.Errorf("Strains.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(strain, want) { + t.Errorf("Strains.Create returned %+v, want %+v", strain, want) + } +} diff --git a/router/api.go b/router/api.go index 74f9e64..cff06b6 100644 --- a/router/api.go +++ b/router/api.go @@ -25,6 +25,7 @@ func API() *mux.Router { m.Path("/species/{Id:.+}").Methods("DELETE").Name(DeleteSpecies) // Strains + m.Path("/strains").Methods("POST").Name(CreateStrain) m.Path("/strains/{Id:.+}").Methods("GET").Name(Strain) return m diff --git a/router/routes.go b/router/routes.go index b3d64b7..6a6fc10 100644 --- a/router/routes.go +++ b/router/routes.go @@ -17,5 +17,6 @@ const ( UpdateSpecies = "species:update" DeleteSpecies = "species:delete" - Strain = "strain:get" + Strain = "strain:get" + CreateStrain = "strain:create" )