From bc3c724a1d1ec2139f933ade347ccca1566e798e Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Tue, 2 Dec 2014 14:37:53 -0900 Subject: [PATCH] Update a measurement --- api/handler.go | 1 + api/measurements.go | 19 +++++++++++++++++++ api/measurements_test.go | 30 ++++++++++++++++++++++++++++++ datastore/measurements.go | 23 +++++++++++++++++++++++ datastore/measurements_test.go | 20 ++++++++++++++++++++ models/measurements.go | 32 ++++++++++++++++++++++++++++++++ models/measurements_test.go | 31 +++++++++++++++++++++++++++++++ router/api.go | 1 + router/routes.go | 1 + 9 files changed, 158 insertions(+) diff --git a/api/handler.go b/api/handler.go index c779938..d5bdaab 100644 --- a/api/handler.go +++ b/api/handler.go @@ -68,6 +68,7 @@ func Handler() *mux.Router { m.Get(router.Measurement).Handler(handler(serveMeasurement)) m.Get(router.CreateMeasurement).Handler(handler(serveCreateMeasurement)) m.Get(router.Measurements).Handler(handler(serveMeasurementList)) + m.Get(router.UpdateMeasurement).Handler(handler(serveUpdateMeasurement)) return m } diff --git a/api/measurements.go b/api/measurements.go index bc97833..8b179e0 100644 --- a/api/measurements.go +++ b/api/measurements.go @@ -57,3 +57,22 @@ func serveMeasurementList(w http.ResponseWriter, r *http.Request) error { return writeJSON(w, measurements) } + +func serveUpdateMeasurement(w http.ResponseWriter, r *http.Request) error { + id, _ := strconv.ParseInt(mux.Vars(r)["Id"], 10, 0) + var measurement models.Measurement + err := json.NewDecoder(r.Body).Decode(&measurement) + if err != nil { + return err + } + + updated, err := store.Measurements.Update(id, &measurement) + if err != nil { + return err + } + if updated { + w.WriteHeader(http.StatusOK) + } + + return writeJSON(w, measurement) +} diff --git a/api/measurements_test.go b/api/measurements_test.go index d8c3df0..de02b40 100644 --- a/api/measurements_test.go +++ b/api/measurements_test.go @@ -94,3 +94,33 @@ func TestMeasurement_List(t *testing.T) { t.Errorf("got measurements %+v but wanted measurements %+v", measurements, want) } } + +func TestMeasurement_Update(t *testing.T) { + setup() + + want := newMeasurement() + + calledPut := false + store.Measurements.(*models.MockMeasurementsService).Update_ = func(id int64, measurement *models.Measurement) (bool, error) { + if id != want.Id { + t.Errorf("wanted request for measurement %d but got %d", want.Id, id) + } + if !normalizeDeepEqual(want, measurement) { + t.Errorf("wanted request for measurement %d but got %d", want, measurement) + } + calledPut = true + return true, nil + } + + success, err := apiClient.Measurements.Update(want.Id, want) + if err != nil { + t.Fatal(err) + } + + if !calledPut { + t.Error("!calledPut") + } + if !success { + t.Error("!success") + } +} diff --git a/datastore/measurements.go b/datastore/measurements.go index 62f97f7..ae01cf4 100644 --- a/datastore/measurements.go +++ b/datastore/measurements.go @@ -46,3 +46,26 @@ func (s *measurementsStore) List(opt *models.MeasurementListOptions) ([]*models. } return measurements, nil } + +func (s *measurementsStore) Update(id int64, measurement *models.Measurement) (bool, error) { + _, err := s.Get(id) + if err != nil { + return false, err + } + + if id != measurement.Id { + return false, models.ErrMeasurementNotFound + } + + measurement.UpdatedAt = time.Now() + changed, err := s.dbh.Update(measurement) + if err != nil { + return false, err + } + + if changed == 0 { + return false, ErrNoRowsUpdated + } + + return true, nil +} diff --git a/datastore/measurements_test.go b/datastore/measurements_test.go index 546da65..fda3890 100644 --- a/datastore/measurements_test.go +++ b/datastore/measurements_test.go @@ -98,3 +98,23 @@ func TestMeasurementsStore_List_db(t *testing.T) { t.Errorf("got measurements %+v, want %+v", measurements, want) } } + +func TestMeasurementsStore_Update_db(t *testing.T) { + tx, _ := DB.Begin() + defer tx.Rollback() + + measurement := insertMeasurement(t, tx) + + d := NewDatastore(tx) + + // Tweak it + measurement.MeasurementValue = sql.NullFloat64{Float64: 4.56, Valid: true} + updated, err := d.Measurements.Update(measurement.Id, measurement) + if err != nil { + t.Fatal(err) + } + + if !updated { + t.Error("!updated") + } +} diff --git a/models/measurements.go b/models/measurements.go index 7845154..849f209 100644 --- a/models/measurements.go +++ b/models/measurements.go @@ -43,6 +43,9 @@ type MeasurementsService interface { // Create a measurement Create(measurement *Measurement) (bool, error) + + // Update an existing measurement + Update(id int64, MeasurementType *Measurement) (bool, error) } var ( @@ -118,10 +121,32 @@ func (s *measurementsService) List(opt *MeasurementListOptions) ([]*Measurement, return measurements, nil } +func (s *measurementsService) Update(id int64, measurement *Measurement) (bool, error) { + strId := strconv.FormatInt(id, 10) + + url, err := s.client.url(router.UpdateMeasurement, map[string]string{"Id": strId}, nil) + if err != nil { + return false, err + } + + req, err := s.client.NewRequest("PUT", url.String(), measurement) + if err != nil { + return false, err + } + + resp, err := s.client.Do(req, &measurement) + if err != nil { + return false, err + } + + return resp.StatusCode == http.StatusOK, nil +} + type MockMeasurementsService struct { Get_ func(id int64) (*Measurement, error) List_ func(opt *MeasurementListOptions) ([]*Measurement, error) Create_ func(measurement *Measurement) (bool, error) + Update_ func(id int64, measurement *Measurement) (bool, error) } var _ MeasurementsService = &MockMeasurementsService{} @@ -146,3 +171,10 @@ func (s *MockMeasurementsService) List(opt *MeasurementListOptions) ([]*Measurem } return s.List_(opt) } + +func (s *MockMeasurementsService) Update(id int64, measurement *Measurement) (bool, error) { + if s.Update_ == nil { + return false, nil + } + return s.Update_(id, measurement) +} diff --git a/models/measurements_test.go b/models/measurements_test.go index 0d28403..69b14d7 100644 --- a/models/measurements_test.go +++ b/models/measurements_test.go @@ -116,3 +116,34 @@ func TestMeasurementService_List(t *testing.T) { t.Errorf("Measurements.List return %+v, want %+v", measurements, want) } } + +func TestMeasurementService_Update(t *testing.T) { + setup() + defer teardown() + + want := newMeasurement() + + var called bool + mux.HandleFunc(urlPath(t, router.UpdateMeasurement, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { + called = true + testMethod(t, r, "PUT") + testBody(t, r, `{"id":1,"strainId":1,"observationId":1,"textMeasurementTypeId":{"Int64":0,"Valid":false},"measurementValue":{"Float64":4.56,"Valid":true},"confidenceInterval":{"Float64":0,"Valid":false},"unitTypeId":{"Int64":1,"Valid":true},"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + w.WriteHeader(http.StatusOK) + writeJSON(w, want) + }) + + measurement := newMeasurement() + measurement.MeasurementValue = sql.NullFloat64{Float64: 4.56, Valid: true} + updated, err := client.Measurements.Update(measurement.Id, measurement) + if err != nil { + t.Errorf("Measurements.Update returned error: %v", err) + } + + if !updated { + t.Error("!updated") + } + + if !called { + t.Fatal("!called") + } +} diff --git a/router/api.go b/router/api.go index c64c4ef..3d2a2da 100644 --- a/router/api.go +++ b/router/api.go @@ -63,6 +63,7 @@ func API() *mux.Router { m.Path("/measurements/").Methods("GET").Name(Measurements) m.Path("/measurements/").Methods("POST").Name(CreateMeasurement) m.Path("/measurements/{Id:.+}").Methods("GET").Name(Measurement) + m.Path("/measurements/{Id:.+}").Methods("PUT").Name(UpdateMeasurement) return m } diff --git a/router/routes.go b/router/routes.go index c321684..3f8d7a7 100644 --- a/router/routes.go +++ b/router/routes.go @@ -50,4 +50,5 @@ const ( Measurement = "measurements:get" CreateMeasurement = "measurements:create" Measurements = "measurements:list" + UpdateMeasurement = "measurements:update" )