diff --git a/api/handler.go b/api/handler.go index b7999d8..fd686c6 100644 --- a/api/handler.go +++ b/api/handler.go @@ -33,6 +33,7 @@ func Handler() *mux.Router { m.Get(router.CreateSpecies).Handler(handler(serveCreateSpecies)) m.Get(router.SpeciesList).Handler(handler(serveSpeciesList)) m.Get(router.UpdateSpecies).Handler(handler(serveUpdateSpecies)) + m.Get(router.DeleteSpecies).Handler(handler(serveDeleteSpecies)) return m } diff --git a/api/species.go b/api/species.go index 1e218a4..fe0b7c3 100644 --- a/api/species.go +++ b/api/species.go @@ -76,3 +76,17 @@ func serveUpdateSpecies(w http.ResponseWriter, r *http.Request) error { return writeJSON(w, species) } + +func serveDeleteSpecies(w http.ResponseWriter, r *http.Request) error { + id, _ := strconv.ParseInt(mux.Vars(r)["Id"], 10, 0) + + deleted, err := store.Species.Delete(id) + if err != nil { + return err + } + if deleted { + w.WriteHeader(http.StatusOK) + } + + return writeJSON(w, &models.Species{}) +} diff --git a/api/species_test.go b/api/species_test.go index a0c003a..418091e 100644 --- a/api/species_test.go +++ b/api/species_test.go @@ -118,3 +118,30 @@ func TestSpecies_Update(t *testing.T) { t.Error("!success") } } + +func TestSpecies_Delete(t *testing.T) { + setup() + + want := &models.Species{Id: 1, GenusId: 1, SpeciesName: "Test Species"} + + calledDelete := false + store.Species.(*models.MockSpeciesService).Delete_ = func(id int64) (bool, error) { + if id != want.Id { + t.Errorf("wanted request for species %d but got %d", want.Id, id) + } + calledDelete = true + return true, nil + } + + success, err := apiClient.Species.Delete(1) + if err != nil { + t.Fatal(err) + } + + if !calledDelete { + t.Error("!calledDelete") + } + if !success { + t.Error("!success") + } +} diff --git a/datastore/species.go b/datastore/species.go index c3087c4..651c3e0 100644 --- a/datastore/species.go +++ b/datastore/species.go @@ -61,3 +61,19 @@ func (s *speciesStore) Update(id int64, species *models.Species) (bool, error) { return true, nil } + +func (s *speciesStore) Delete(id int64) (bool, error) { + species, err := s.Get(id) + if err != nil { + return false, err + } + + deleted, err := s.dbh.Delete(species) + if err != nil { + return false, err + } + if deleted == 0 { + return false, ErrNoRowsDeleted + } + return true, nil +} diff --git a/datastore/species_test.go b/datastore/species_test.go index 1d7c3cd..5839e95 100644 --- a/datastore/species_test.go +++ b/datastore/species_test.go @@ -13,6 +13,7 @@ func TestSpeciesStore_Get_db(t *testing.T) { // Test on a clean database tx.Exec(`DELETE FROM species;`) + tx.Exec(`DELETE FROM genera;`) wantGenus := &models.Genus{GenusName: "Test Genus"} if err := tx.Insert(wantGenus); err != nil { @@ -42,6 +43,7 @@ func TestSpeciesStore_Create_db(t *testing.T) { // Test on a clean database tx.Exec(`DELETE FROM species;`) + tx.Exec(`DELETE FROM genera;`) genus := &models.Genus{} if err := tx.Insert(genus); err != nil { @@ -70,6 +72,7 @@ func TestSpeciesStore_List_db(t *testing.T) { // Test on a clean database tx.Exec(`DELETE FROM species;`) + tx.Exec(`DELETE FROM genera;`) genus := &models.Genus{} @@ -103,6 +106,7 @@ func TestSpeciesStore_Update_db(t *testing.T) { // Test on a clean database tx.Exec(`DELETE FROM species;`) + tx.Exec(`DELETE FROM genera;`) d := NewDatastore(nil) // Add a new record @@ -131,3 +135,38 @@ func TestSpeciesStore_Update_db(t *testing.T) { t.Error("!updated") } } + +func TestSpeciesStore_Delete_db(t *testing.T) { + tx, _ := DB.Begin() + defer tx.Rollback() + + // Test on a clean database + tx.Exec(`DELETE FROM species;`) + tx.Exec(`DELETE FROM genera;`) + + d := NewDatastore(tx) + // Add a new record + genus := &models.Genus{GenusName: "Test Genus"} + _, err := d.Genera.Create(genus) + if err != nil { + t.Fatal(err) + } + species := &models.Species{GenusId: genus.Id, SpeciesName: "Test Species"} + created, err := d.Species.Create(species) + if err != nil { + t.Fatal(err) + } + if !created { + t.Error("!created") + } + + // Delete it + deleted, err := d.Species.Delete(species.Id) + if err != nil { + t.Fatal(err) + } + + if !deleted { + t.Error("!delete") + } +} diff --git a/models/species.go b/models/species.go index b742de8..19b69fb 100644 --- a/models/species.go +++ b/models/species.go @@ -32,6 +32,9 @@ type SpeciesService interface { // Update an existing species Update(id int64, species *Species) (updated bool, err error) + + // Delete an existing species + Delete(id int64) (deleted bool, err error) } var ( @@ -129,11 +132,34 @@ func (s *speciesService) Update(id int64, species *Species) (bool, error) { return resp.StatusCode == http.StatusOK, nil } +func (s *speciesService) Delete(id int64) (bool, error) { + strId := strconv.FormatInt(id, 10) + + url, err := s.client.url(router.DeleteSpecies, map[string]string{"Id": strId}, nil) + if err != nil { + return false, err + } + + req, err := s.client.NewRequest("DELETE", url.String(), nil) + if err != nil { + return false, err + } + + var species *Species + resp, err := s.client.Do(req, &species) + if err != nil { + return false, err + } + + return resp.StatusCode == http.StatusOK, nil +} + type MockSpeciesService struct { Get_ func(id int64) (*Species, error) List_ func(opt *SpeciesListOptions) ([]*Species, error) Create_ func(species *Species) (bool, error) Update_ func(id int64, species *Species) (bool, error) + Delete_ func(id int64) (bool, error) } var _ SpeciesService = &MockSpeciesService{} @@ -165,3 +191,10 @@ func (s *MockSpeciesService) Update(id int64, species *Species) (bool, error) { } return s.Update_(id, species) } + +func (s *MockSpeciesService) Delete(id int64) (bool, error) { + if s.Delete_ == nil { + return false, nil + } + return s.Delete_(id) +} diff --git a/models/species_test.go b/models/species_test.go index cb66f9e..f2ef1e9 100644 --- a/models/species_test.go +++ b/models/species_test.go @@ -136,3 +136,32 @@ func TestSpeciesService_Update(t *testing.T) { t.Fatal("!called") } } + +func TestSpeciesService_Delete(t *testing.T) { + setup() + defer teardown() + + want := &Species{Id: 1, GenusId: 1, SpeciesName: "Test Species"} + + var called bool + mux.HandleFunc(urlPath(t, router.DeleteSpecies, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { + called = true + testMethod(t, r, "DELETE") + + w.WriteHeader(http.StatusOK) + writeJSON(w, want) + }) + + deleted, err := client.Species.Delete(1) + if err != nil { + t.Errorf("Species.Delete returned error: %v", err) + } + + if !deleted { + t.Error("!deleted") + } + + if !called { + t.Fatal("!called") + } +} diff --git a/router/api.go b/router/api.go index 5bf24d2..a7731ab 100644 --- a/router/api.go +++ b/router/api.go @@ -22,6 +22,7 @@ func API() *mux.Router { m.Path("/species").Methods("POST").Name(CreateSpecies) m.Path("/species/{Id:.+}").Methods("GET").Name(Species) m.Path("/species/{Id:.+}").Methods("PUT").Name(UpdateSpecies) + m.Path("/species/{Id:.+}").Methods("DELETE").Name(DeleteSpecies) return m } diff --git a/router/routes.go b/router/routes.go index 0db9205..124dd98 100644 --- a/router/routes.go +++ b/router/routes.go @@ -15,4 +15,5 @@ const ( CreateSpecies = "species:create" SpeciesList = "species:list" UpdateSpecies = "species:update" + DeleteSpecies = "species:delete" )