Create a new observation
This commit is contained in:
		
							parent
							
								
									a018d2bd7a
								
							
						
					
					
						commit
						f867e5c424
					
				
					 9 changed files with 154 additions and 3 deletions
				
			
		|  | @ -48,6 +48,7 @@ func Handler() *mux.Router { | ||||||
| 	m.Get(router.DeleteObservationType).Handler(handler(serveDeleteObservationType)) | 	m.Get(router.DeleteObservationType).Handler(handler(serveDeleteObservationType)) | ||||||
| 
 | 
 | ||||||
| 	m.Get(router.Observation).Handler(handler(serveObservation)) | 	m.Get(router.Observation).Handler(handler(serveObservation)) | ||||||
|  | 	m.Get(router.CreateObservation).Handler(handler(serveCreateObservation)) | ||||||
| 
 | 
 | ||||||
| 	return m | 	return m | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,10 +1,12 @@ | ||||||
| package api | package api | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gorilla/mux" | 	"github.com/gorilla/mux" | ||||||
|  | 	"github.com/thermokarst/bactdb/models" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func serveObservation(w http.ResponseWriter, r *http.Request) error { | func serveObservation(w http.ResponseWriter, r *http.Request) error { | ||||||
|  | @ -20,3 +22,21 @@ func serveObservation(w http.ResponseWriter, r *http.Request) error { | ||||||
| 
 | 
 | ||||||
| 	return writeJSON(w, observation) | 	return writeJSON(w, observation) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func serveCreateObservation(w http.ResponseWriter, r *http.Request) error { | ||||||
|  | 	var observation models.Observation | ||||||
|  | 	err := json.NewDecoder(r.Body).Decode(&observation) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	created, err := store.Observations.Create(&observation) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if created { | ||||||
|  | 		w.WriteHeader(http.StatusCreated) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return writeJSON(w, observation) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -38,3 +38,30 @@ func TestObservation_Get(t *testing.T) { | ||||||
| 		t.Errorf("got %+v but wanted %+v", got, want) | 		t.Errorf("got %+v but wanted %+v", got, want) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestObservation_Create(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 
 | ||||||
|  | 	want := newObservation() | ||||||
|  | 
 | ||||||
|  | 	calledPost := false | ||||||
|  | 	store.Observations.(*models.MockObservationsService).Create_ = func(observation *models.Observation) (bool, error) { | ||||||
|  | 		if !normalizeDeepEqual(want, observation) { | ||||||
|  | 			t.Errorf("wanted request for observation %d but got %d", want, observation) | ||||||
|  | 		} | ||||||
|  | 		calledPost = true | ||||||
|  | 		return true, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	success, err := apiClient.Observations.Create(want) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if !calledPost { | ||||||
|  | 		t.Error("!calledPost") | ||||||
|  | 	} | ||||||
|  | 	if !success { | ||||||
|  | 		t.Error("!success") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,10 @@ | ||||||
| package datastore | package datastore | ||||||
| 
 | 
 | ||||||
| import "github.com/thermokarst/bactdb/models" | import ( | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"github.com/thermokarst/bactdb/models" | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	DB.AddTableWithName(models.Observation{}, "observations").SetKeys(true, "Id") | 	DB.AddTableWithName(models.Observation{}, "observations").SetKeys(true, "Id") | ||||||
|  | @ -20,3 +24,13 @@ func (s *observationsStore) Get(id int64) (*models.Observation, error) { | ||||||
| 	} | 	} | ||||||
| 	return observation[0], nil | 	return observation[0], nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (s *observationsStore) Create(observation *models.Observation) (bool, error) { | ||||||
|  | 	currentTime := time.Now() | ||||||
|  | 	observation.CreatedAt = currentTime | ||||||
|  | 	observation.UpdatedAt = currentTime | ||||||
|  | 	if err := s.dbh.Insert(observation); err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -45,3 +45,23 @@ func TestObservationsStore_Get_db(t *testing.T) { | ||||||
| 		t.Errorf("got observation %+v, want %+v", observation, want) | 		t.Errorf("got observation %+v, want %+v", observation, want) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestObservationsStore_Create_db(t *testing.T) { | ||||||
|  | 	tx, _ := DB.Begin() | ||||||
|  | 	defer tx.Rollback() | ||||||
|  | 
 | ||||||
|  | 	observation := newObservation(t, tx) | ||||||
|  | 
 | ||||||
|  | 	d := NewDatastore(tx) | ||||||
|  | 
 | ||||||
|  | 	created, err := d.Observations.Create(observation) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if !created { | ||||||
|  | 		t.Error("!created") | ||||||
|  | 	} | ||||||
|  | 	if observation.Id == 0 { | ||||||
|  | 		t.Error("want nonzero observation.Id after submitting") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ package models | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"net/http" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | @ -28,6 +29,9 @@ func NewObservation() *Observation { | ||||||
| type ObservationsService interface { | type ObservationsService interface { | ||||||
| 	// Get an observation | 	// Get an observation | ||||||
| 	Get(id int64) (*Observation, error) | 	Get(id int64) (*Observation, error) | ||||||
|  | 
 | ||||||
|  | 	// Create an observation | ||||||
|  | 	Create(observation *Observation) (bool, error) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
|  | @ -60,8 +64,28 @@ func (s *observationsService) Get(id int64) (*Observation, error) { | ||||||
| 	return observation, nil | 	return observation, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (s *observationsService) Create(observation *Observation) (bool, error) { | ||||||
|  | 	url, err := s.client.url(router.CreateObservation, nil, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	req, err := s.client.NewRequest("POST", url.String(), observation) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	resp, err := s.client.Do(req, &observation) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return resp.StatusCode == http.StatusCreated, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type MockObservationsService struct { | type MockObservationsService struct { | ||||||
| 	Get_    func(id int64) (*Observation, error) | 	Get_    func(id int64) (*Observation, error) | ||||||
|  | 	Create_ func(observation *Observation) (bool, error) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var _ObservationsService = &MockObservationsService{} | var _ObservationsService = &MockObservationsService{} | ||||||
|  | @ -72,3 +96,10 @@ func (s *MockObservationsService) Get(id int64) (*Observation, error) { | ||||||
| 	} | 	} | ||||||
| 	return s.Get_(id) | 	return s.Get_(id) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (s *MockObservationsService) Create(observation *Observation) (bool, error) { | ||||||
|  | 	if s.Create_ == nil { | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  | 	return s.Create_(observation) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -43,3 +43,39 @@ func TestObservationService_Get(t *testing.T) { | ||||||
| 		t.Errorf("Observations.Get return %+v, want %+v", observation, want) | 		t.Errorf("Observations.Get return %+v, want %+v", observation, want) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestObservationService_Create(t *testing.T) { | ||||||
|  | 	setup() | ||||||
|  | 	defer teardown() | ||||||
|  | 
 | ||||||
|  | 	want := newObservation() | ||||||
|  | 
 | ||||||
|  | 	var called bool | ||||||
|  | 	mux.HandleFunc(urlPath(t, router.CreateObservation, nil), func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		called = true | ||||||
|  | 		testMethod(t, r, "POST") | ||||||
|  | 		testBody(t, r, `{"id":1,"observation_name":"Test Observation","observation_type_id":0,"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","deleted_at":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") | ||||||
|  | 
 | ||||||
|  | 		w.WriteHeader(http.StatusCreated) | ||||||
|  | 		writeJSON(w, want) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	observation := newObservation() | ||||||
|  | 	created, err := client.Observations.Create(observation) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("Observations.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(observation, want) { | ||||||
|  | 		t.Errorf("Observations.Create returned %+v, want %+v", observation, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -39,6 +39,7 @@ func API() *mux.Router { | ||||||
| 	m.Path("/observation_types/{Id:.+}").Methods("DELETE").Name(DeleteObservationType) | 	m.Path("/observation_types/{Id:.+}").Methods("DELETE").Name(DeleteObservationType) | ||||||
| 
 | 
 | ||||||
| 	// Observations | 	// Observations | ||||||
|  | 	m.Path("/observations").Methods("POST").Name(CreateObservation) | ||||||
| 	m.Path("/observations/{Id:.+}").Methods("GET").Name(Observation) | 	m.Path("/observations/{Id:.+}").Methods("GET").Name(Observation) | ||||||
| 
 | 
 | ||||||
| 	return m | 	return m | ||||||
|  |  | ||||||
|  | @ -30,4 +30,5 @@ const ( | ||||||
| 	DeleteObservationType = "observation_type:delete" | 	DeleteObservationType = "observation_type:delete" | ||||||
| 
 | 
 | ||||||
| 	Observation       = "observation:get" | 	Observation       = "observation:get" | ||||||
|  | 	CreateObservation = "observation:create" | ||||||
| ) | ) | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Matthew Dillon
						Matthew Dillon