From 3e0ca9df8c1141381f339fdea973169c5de625cc Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Wed, 26 Nov 2014 21:56:25 -0900 Subject: [PATCH] Get a text measurement type --- api/handler.go | 2 + api/text_measurement_types.go | 22 +++++++ api/text_measurement_types_test.go | 40 +++++++++++++ datastore/datastore.go | 29 +++++----- datastore/text_measurement_types.go | 22 +++++++ datastore/text_measurement_types_test.go | 44 ++++++++++++++ models/client.go | 14 +++-- models/text_measurement_types.go | 73 ++++++++++++++++++++++++ models/text_measurement_types_test.go | 45 +++++++++++++++ router/api.go | 3 + router/routes.go | 2 + 11 files changed, 277 insertions(+), 19 deletions(-) create mode 100644 api/text_measurement_types.go create mode 100644 api/text_measurement_types_test.go create mode 100644 datastore/text_measurement_types.go create mode 100644 datastore/text_measurement_types_test.go create mode 100644 models/text_measurement_types.go create mode 100644 models/text_measurement_types_test.go diff --git a/api/handler.go b/api/handler.go index e128b13..a4be240 100644 --- a/api/handler.go +++ b/api/handler.go @@ -53,6 +53,8 @@ func Handler() *mux.Router { m.Get(router.UpdateObservation).Handler(handler(serveUpdateObservation)) m.Get(router.DeleteObservation).Handler(handler(serveDeleteObservation)) + m.Get(router.TextMeasurementType).Handler(handler(serveTextMeasurementType)) + return m } diff --git a/api/text_measurement_types.go b/api/text_measurement_types.go new file mode 100644 index 0000000..e454cbc --- /dev/null +++ b/api/text_measurement_types.go @@ -0,0 +1,22 @@ +package api + +import ( + "net/http" + "strconv" + + "github.com/gorilla/mux" +) + +func serveTextMeasurementType(w http.ResponseWriter, r *http.Request) error { + id, err := strconv.ParseInt(mux.Vars(r)["Id"], 10, 0) + if err != nil { + return err + } + + text_measurement_type, err := store.TextMeasurementTypes.Get(id) + if err != nil { + return err + } + + return writeJSON(w, text_measurement_type) +} diff --git a/api/text_measurement_types_test.go b/api/text_measurement_types_test.go new file mode 100644 index 0000000..95d0bee --- /dev/null +++ b/api/text_measurement_types_test.go @@ -0,0 +1,40 @@ +package api + +import ( + "testing" + + "github.com/thermokarst/bactdb/models" +) + +func newTextMeasurementType() *models.TextMeasurementType { + text_measurement_type := models.NewTextMeasurementType() + return text_measurement_type +} + +func TestTextMeasurementType_Get(t *testing.T) { + setup() + + want := newTextMeasurementType() + + calledGet := false + + store.TextMeasurementTypes.(*models.MockTextMeasurementTypesService).Get_ = func(id int64) (*models.TextMeasurementType, error) { + if id != want.Id { + t.Errorf("wanted request for text_measurement_type %d but got %d", want.Id, id) + } + calledGet = true + return want, nil + } + + got, err := apiClient.TextMeasurementTypes.Get(want.Id) + if err != nil { + t.Fatal(err) + } + + if !calledGet { + t.Error("!calledGet") + } + if !normalizeDeepEqual(want, got) { + t.Errorf("got %+v but wanted %+v", got, want) + } +} diff --git a/datastore/datastore.go b/datastore/datastore.go index f18afb1..c4de884 100644 --- a/datastore/datastore.go +++ b/datastore/datastore.go @@ -9,13 +9,14 @@ import ( // A datastore access point (in PostgreSQL) type Datastore struct { - Users models.UsersService - Genera models.GeneraService - Species models.SpeciesService - Strains models.StrainsService - ObservationTypes models.ObservationTypesService - Observations models.ObservationsService - dbh modl.SqlExecutor + Users models.UsersService + Genera models.GeneraService + Species models.SpeciesService + Strains models.StrainsService + ObservationTypes models.ObservationTypesService + Observations models.ObservationsService + TextMeasurementTypes models.TextMeasurementTypesService + dbh modl.SqlExecutor } var ( @@ -37,16 +38,18 @@ func NewDatastore(dbh modl.SqlExecutor) *Datastore { d.Strains = &strainsStore{d} d.ObservationTypes = &observationTypesStore{d} d.Observations = &observationsStore{d} + d.TextMeasurementTypes = &textMeasurementTypesStore{d} return d } func NewMockDatastore() *Datastore { return &Datastore{ - Users: &models.MockUsersService{}, - Genera: &models.MockGeneraService{}, - Species: &models.MockSpeciesService{}, - Strains: &models.MockStrainsService{}, - ObservationTypes: &models.MockObservationTypesService{}, - Observations: &models.MockObservationsService{}, + Users: &models.MockUsersService{}, + Genera: &models.MockGeneraService{}, + Species: &models.MockSpeciesService{}, + Strains: &models.MockStrainsService{}, + ObservationTypes: &models.MockObservationTypesService{}, + Observations: &models.MockObservationsService{}, + TextMeasurementTypes: &models.MockTextMeasurementTypesService{}, } } diff --git a/datastore/text_measurement_types.go b/datastore/text_measurement_types.go new file mode 100644 index 0000000..ae83c56 --- /dev/null +++ b/datastore/text_measurement_types.go @@ -0,0 +1,22 @@ +package datastore + +import "github.com/thermokarst/bactdb/models" + +func init() { + DB.AddTableWithName(models.TextMeasurementType{}, "text_measurement_types").SetKeys(true, "Id") +} + +type textMeasurementTypesStore struct { + *Datastore +} + +func (s *textMeasurementTypesStore) Get(id int64) (*models.TextMeasurementType, error) { + var text_measurement_type []*models.TextMeasurementType + if err := s.dbh.Select(&text_measurement_type, `SELECT * FROM text_measurement_types WHERE id=$1;`, id); err != nil { + return nil, err + } + if len(text_measurement_type) == 0 { + return nil, models.ErrTextMeasurementTypeNotFound + } + return text_measurement_type[0], nil +} diff --git a/datastore/text_measurement_types_test.go b/datastore/text_measurement_types_test.go new file mode 100644 index 0000000..d79e3d9 --- /dev/null +++ b/datastore/text_measurement_types_test.go @@ -0,0 +1,44 @@ +package datastore + +import ( + "reflect" + "testing" + + "github.com/jmoiron/modl" + "github.com/thermokarst/bactdb/models" +) + +func insertTextMeasurementType(t *testing.T, tx *modl.Transaction) *models.TextMeasurementType { + // clean up our target table + tx.Exec(`DELETE FROM text_measurement_types;`) + text_measurement_type := newTextMeasurementType(t, tx) + if err := tx.Insert(text_measurement_type); err != nil { + t.Fatal(err) + } + return text_measurement_type +} + +func newTextMeasurementType(t *testing.T, tx *modl.Transaction) *models.TextMeasurementType { + return &models.TextMeasurementType{TextMeasurementName: "Test Text Measurement Type"} +} + +func TestTextMeasurementTypesStore_Get_db(t *testing.T) { + tx, _ := DB.Begin() + defer tx.Rollback() + + want := insertTextMeasurementType(t, tx) + + d := NewDatastore(tx) + + text_measurement_type, err := d.TextMeasurementTypes.Get(want.Id) + if err != nil { + t.Fatal(err) + } + + normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt) + normalizeTime(&text_measurement_type.CreatedAt, &text_measurement_type.UpdatedAt, &text_measurement_type.DeletedAt) + + if !reflect.DeepEqual(text_measurement_type, want) { + t.Errorf("got text_measurement_type %+v, want %+v", text_measurement_type, want) + } +} diff --git a/models/client.go b/models/client.go index e51c379..df479bb 100644 --- a/models/client.go +++ b/models/client.go @@ -16,12 +16,13 @@ import ( // A Client communicates with bactdb's HTTP API. type Client struct { - Users UsersService - Genera GeneraService - Species SpeciesService - Strains StrainsService - ObservationTypes ObservationTypesService - Observations ObservationsService + Users UsersService + Genera GeneraService + Species SpeciesService + Strains StrainsService + ObservationTypes ObservationTypesService + Observations ObservationsService + TextMeasurementTypes TextMeasurementTypesService // BaseURL for HTTP requests to bactdb's API. BaseURL *url.URL @@ -55,6 +56,7 @@ func NewClient(httpClient *http.Client) *Client { c.Strains = &strainsService{c} c.ObservationTypes = &observationTypesService{c} c.Observations = &observationsService{c} + c.TextMeasurementTypes = &textMeasurementTypesService{c} return c } diff --git a/models/text_measurement_types.go b/models/text_measurement_types.go new file mode 100644 index 0000000..4b0a70a --- /dev/null +++ b/models/text_measurement_types.go @@ -0,0 +1,73 @@ +package models + +import ( + "errors" + "strconv" + "time" + + "github.com/lib/pq" + "github.com/thermokarst/bactdb/router" +) + +// A TextMeasurementType is a lookup type +type TextMeasurementType struct { + Id int64 `json:"id,omitempty"` + TextMeasurementName string `db:"text_measurement_name" json:"textMeasurementName"` + CreatedAt time.Time `db:"created_at" json:"createdAt"` + UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` + DeletedAt pq.NullTime `db:"deleted_at" json:"deletedAt"` +} + +func NewTextMeasurementType() *TextMeasurementType { + return &TextMeasurementType{ + TextMeasurementName: "Test Text Measurement Type", + } +} + +type TextMeasurementTypesService interface { + // Get a text measurement type + Get(id int64) (*TextMeasurementType, error) +} + +var ( + ErrTextMeasurementTypeNotFound = errors.New("text measurement type not found") +) + +type textMeasurementTypesService struct { + client *Client +} + +func (s *textMeasurementTypesService) Get(id int64) (*TextMeasurementType, error) { + strId := strconv.FormatInt(id, 10) + + url, err := s.client.url(router.TextMeasurementType, map[string]string{"Id": strId}, nil) + if err != nil { + return nil, err + } + + req, err := s.client.NewRequest("GET", url.String(), nil) + if err != nil { + return nil, err + } + + var text_measurement_type *TextMeasurementType + _, err = s.client.Do(req, &text_measurement_type) + if err != nil { + return nil, err + } + + return text_measurement_type, nil +} + +type MockTextMeasurementTypesService struct { + Get_ func(id int64) (*TextMeasurementType, error) +} + +var _ TextMeasurementTypesService = &MockTextMeasurementTypesService{} + +func (s *MockTextMeasurementTypesService) Get(id int64) (*TextMeasurementType, error) { + if s.Get_ == nil { + return nil, nil + } + return s.Get_(id) +} diff --git a/models/text_measurement_types_test.go b/models/text_measurement_types_test.go new file mode 100644 index 0000000..c9cfd2a --- /dev/null +++ b/models/text_measurement_types_test.go @@ -0,0 +1,45 @@ +package models + +import ( + "net/http" + "reflect" + "testing" + + "github.com/thermokarst/bactdb/router" +) + +func newTextMeasurementType() *TextMeasurementType { + text_measurement_type := NewTextMeasurementType() + text_measurement_type.Id = 1 + return text_measurement_type +} + +func TestTextMeasurementTypeService_Get(t *testing.T) { + setup() + defer teardown() + + want := newTextMeasurementType() + + var called bool + mux.HandleFunc(urlPath(t, router.TextMeasurementType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { + called = true + testMethod(t, r, "GET") + + writeJSON(w, want) + }) + + text_measurement_type, err := client.TextMeasurementTypes.Get(want.Id) + if err != nil { + t.Errorf("TextMeasurementTypes.Get returned error: %v", err) + } + + if !called { + t.Fatal("!called") + } + + normalizeTime(&want.CreatedAt, &want.UpdatedAt, &want.DeletedAt) + + if !reflect.DeepEqual(text_measurement_type, want) { + t.Errorf("TextMeasurementTypes.Get return %+v, want %+v", text_measurement_type, want) + } +} diff --git a/router/api.go b/router/api.go index 8e17751..adbac9c 100644 --- a/router/api.go +++ b/router/api.go @@ -45,5 +45,8 @@ func API() *mux.Router { m.Path("/observations/{Id:.+}").Methods("PUT").Name(UpdateObservation) m.Path("/observations/{Id:.+}").Methods("DELETE").Name(DeleteObservation) + // TextMeasurementTypes + m.Path("/text_measurement_types/{Id:.+}").Methods("GET").Name(TextMeasurementType) + return m } diff --git a/router/routes.go b/router/routes.go index fa97b94..381b576 100644 --- a/router/routes.go +++ b/router/routes.go @@ -34,4 +34,6 @@ const ( Observations = "observation:list" UpdateObservation = "observation:update" DeleteObservation = "observation:delete" + + TextMeasurementType = "text_measurement_type:get" )