diff --git a/api/handler.go b/api/handler.go index 28e1df5..c779938 100644 --- a/api/handler.go +++ b/api/handler.go @@ -67,6 +67,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)) return m } diff --git a/api/measurements.go b/api/measurements.go index de9e901..bc97833 100644 --- a/api/measurements.go +++ b/api/measurements.go @@ -40,3 +40,20 @@ func serveCreateMeasurement(w http.ResponseWriter, r *http.Request) error { return writeJSON(w, measurement) } + +func serveMeasurementList(w http.ResponseWriter, r *http.Request) error { + var opt models.MeasurementListOptions + if err := schemaDecoder.Decode(&opt, r.URL.Query()); err != nil { + return err + } + + measurements, err := store.Measurements.List(&opt) + if err != nil { + return err + } + if measurements == nil { + measurements = []*models.Measurement{} + } + + return writeJSON(w, measurements) +} diff --git a/api/measurements_test.go b/api/measurements_test.go index a2dcb42..d8c3df0 100644 --- a/api/measurements_test.go +++ b/api/measurements_test.go @@ -65,3 +65,32 @@ func TestMeasurement_Create(t *testing.T) { t.Error("!success") } } + +func TestMeasurement_List(t *testing.T) { + setup() + + want := []*models.Measurement{newMeasurement()} + wantOpt := &models.MeasurementListOptions{ListOptions: models.ListOptions{Page: 1, PerPage: 10}} + + calledList := false + store.Measurements.(*models.MockMeasurementsService).List_ = func(opt *models.MeasurementListOptions) ([]*models.Measurement, error) { + if !normalizeDeepEqual(wantOpt, opt) { + t.Errorf("wanted options %d but got %d", wantOpt, opt) + } + calledList = true + return want, nil + } + + measurements, err := apiClient.Measurements.List(wantOpt) + if err != nil { + t.Fatal(err) + } + + if !calledList { + t.Error("!calledList") + } + + if !normalizeDeepEqual(&want, &measurements) { + t.Errorf("got measurements %+v but wanted measurements %+v", measurements, want) + } +} diff --git a/datastore/measurements.go b/datastore/measurements.go index b6d2b93..62f97f7 100644 --- a/datastore/measurements.go +++ b/datastore/measurements.go @@ -34,3 +34,15 @@ func (s *measurementsStore) Create(measurement *models.Measurement) (bool, error } return true, nil } + +func (s *measurementsStore) List(opt *models.MeasurementListOptions) ([]*models.Measurement, error) { + if opt == nil { + opt = &models.MeasurementListOptions{} + } + var measurements []*models.Measurement + err := s.dbh.Select(&measurements, `SELECT * FROM measurements LIMIT $1 OFFSET $2;`, opt.PerPageOrDefault(), opt.Offset()) + if err != nil { + return nil, err + } + return measurements, nil +} diff --git a/datastore/measurements_test.go b/datastore/measurements_test.go index 92cbc31..546da65 100644 --- a/datastore/measurements_test.go +++ b/datastore/measurements_test.go @@ -75,3 +75,26 @@ func TestMeasurementsStore_Create_db(t *testing.T) { t.Error("want nonzero measurement.Id after submitting") } } + +func TestMeasurementsStore_List_db(t *testing.T) { + tx, _ := DB.Begin() + defer tx.Rollback() + + want_measurement := insertMeasurement(t, tx) + want := []*models.Measurement{want_measurement} + + d := NewDatastore(tx) + + measurements, err := d.Measurements.List(&models.MeasurementListOptions{ListOptions: models.ListOptions{Page: 1, PerPage: 10}}) + if err != nil { + t.Fatal(err) + } + + for i := range want { + normalizeTime(&want[i].CreatedAt, &want[i].UpdatedAt, &want[i].DeletedAt) + normalizeTime(&measurements[i].CreatedAt, &measurements[i].UpdatedAt, &measurements[i].DeletedAt) + } + if !reflect.DeepEqual(measurements, want) { + t.Errorf("got measurements %+v, want %+v", measurements, want) + } +} diff --git a/models/measurements.go b/models/measurements.go index f21ee37..7845154 100644 --- a/models/measurements.go +++ b/models/measurements.go @@ -38,6 +38,9 @@ type MeasurementsService interface { // Get a measurement Get(id int64) (*Measurement, error) + // List all measurements + List(opt *MeasurementListOptions) ([]*Measurement, error) + // Create a measurement Create(measurement *Measurement) (bool, error) } @@ -91,8 +94,33 @@ func (s *measurementsService) Create(measurement *Measurement) (bool, error) { return resp.StatusCode == http.StatusCreated, nil } +type MeasurementListOptions struct { + ListOptions +} + +func (s *measurementsService) List(opt *MeasurementListOptions) ([]*Measurement, error) { + url, err := s.client.url(router.Measurements, nil, opt) + if err != nil { + return nil, err + } + + req, err := s.client.NewRequest("GET", url.String(), nil) + if err != nil { + return nil, err + } + + var measurements []*Measurement + _, err = s.client.Do(req, &measurements) + if err != nil { + return nil, err + } + + return measurements, nil +} + type MockMeasurementsService struct { Get_ func(id int64) (*Measurement, error) + List_ func(opt *MeasurementListOptions) ([]*Measurement, error) Create_ func(measurement *Measurement) (bool, error) } @@ -111,3 +139,10 @@ func (s *MockMeasurementsService) Create(measurement *Measurement) (bool, error) } return s.Create_(measurement) } + +func (s *MockMeasurementsService) List(opt *MeasurementListOptions) ([]*Measurement, error) { + if s.List_ == nil { + return nil, nil + } + return s.List_(opt) +} diff --git a/models/measurements_test.go b/models/measurements_test.go index 5ee72d7..0d28403 100644 --- a/models/measurements_test.go +++ b/models/measurements_test.go @@ -83,3 +83,36 @@ func TestMeasurementService_Create(t *testing.T) { t.Errorf("Measurements.Create returned %+v, want %+v", measurement, want) } } + +func TestMeasurementService_List(t *testing.T) { + setup() + defer teardown() + + want := []*Measurement{newMeasurement()} + + var called bool + mux.HandleFunc(urlPath(t, router.Measurements, nil), func(w http.ResponseWriter, r *http.Request) { + called = true + testMethod(t, r, "GET") + testFormValues(t, r, values{}) + + writeJSON(w, want) + }) + + measurements, err := client.Measurements.List(nil) + if err != nil { + t.Errorf("Measurements.List returned error: %v", err) + } + + if !called { + t.Fatal("!called") + } + + for _, u := range want { + normalizeTime(&u.CreatedAt, &u.UpdatedAt, &u.DeletedAt) + } + + if !reflect.DeepEqual(measurements, want) { + t.Errorf("Measurements.List return %+v, want %+v", measurements, want) + } +} diff --git a/router/api.go b/router/api.go index e492940..c64c4ef 100644 --- a/router/api.go +++ b/router/api.go @@ -60,6 +60,7 @@ func API() *mux.Router { m.Path("/unit_types/{Id:.+}").Methods("DELETE").Name(DeleteUnitType) // Measurements + m.Path("/measurements/").Methods("GET").Name(Measurements) m.Path("/measurements/").Methods("POST").Name(CreateMeasurement) m.Path("/measurements/{Id:.+}").Methods("GET").Name(Measurement) diff --git a/router/routes.go b/router/routes.go index 422f5bc..c321684 100644 --- a/router/routes.go +++ b/router/routes.go @@ -47,6 +47,7 @@ const ( UpdateUnitType = "unit_type:update" DeleteUnitType = "unit_type:delete" - Measurement = "measurement:get" + Measurement = "measurements:get" CreateMeasurement = "measurements:create" + Measurements = "measurements:list" )