From 8dc07e3cc854bb7d1eb9eea713d46ef9d7e6975b Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Sat, 6 Dec 2014 14:32:58 -0900 Subject: [PATCH] Cleaning up null values in json --- api/measurements_test.go | 6 ++ datastore/datastore_test.go | 7 +- datastore/measurements_test.go | 6 +- models/client_test.go | 6 +- models/genera.go | 11 +-- models/genera_test.go | 4 +- models/measurements.go | 26 ++--- models/measurements_test.go | 8 +- models/observation_types.go | 11 +-- models/observation_types_test.go | 4 +- models/observations.go | 13 ++- models/observations_test.go | 4 +- models/species.go | 13 ++- models/species_test.go | 4 +- models/strains.go | 24 +++-- models/strains_test.go | 4 +- models/text_measurement_types.go | 11 +-- models/text_measurement_types_test.go | 4 +- models/types.go | 131 ++++++++++++++++++++++++++ models/unit_types.go | 13 ++- models/unit_types_test.go | 4 +- 21 files changed, 222 insertions(+), 92 deletions(-) create mode 100644 models/types.go diff --git a/api/measurements_test.go b/api/measurements_test.go index 2e6848c..a1235e6 100644 --- a/api/measurements_test.go +++ b/api/measurements_test.go @@ -1,6 +1,7 @@ package api import ( + "database/sql" "testing" "github.com/thermokarst/bactdb/models" @@ -8,6 +9,11 @@ import ( func newMeasurement() *models.Measurement { measurement := models.NewMeasurement() + measurement.Id = 1 + measurement.StrainId = 2 + measurement.ObservationId = 3 + measurement.TextMeasurementTypeId = models.NullInt64{sql.NullInt64{Int64: 4, Valid: false}} + measurement.UnitTypeId = models.NullInt64{sql.NullInt64{Int64: 5, Valid: true}} return measurement } diff --git a/datastore/datastore_test.go b/datastore/datastore_test.go index f4f75a4..5ab58dc 100644 --- a/datastore/datastore_test.go +++ b/datastore/datastore_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/lib/pq" + "github.com/thermokarst/bactdb/models" ) func normalizeTime(t ...interface{}) { @@ -15,9 +16,9 @@ func normalizeTime(t ...interface{}) { case *time.Time: x, _ := v.(*time.Time) *x = x.In(time.UTC) - case *pq.NullTime: - x, _ := v.(*pq.NullTime) - *x = pq.NullTime{Time: x.Time.In(time.UTC), Valid: x.Valid} + case *models.NullTime: + x, _ := v.(*models.NullTime) + *x = models.NullTime{pq.NullTime{Time: x.Time.In(time.UTC), Valid: x.Valid}} } } } diff --git a/datastore/measurements_test.go b/datastore/measurements_test.go index 17937e7..fd16e14 100644 --- a/datastore/measurements_test.go +++ b/datastore/measurements_test.go @@ -30,8 +30,8 @@ func newMeasurement(t *testing.T, tx *modl.Transaction) *models.Measurement { return &models.Measurement{ StrainId: strain.Id, ObservationId: observation.Id, - NumValue: sql.NullFloat64{Float64: 1.23, Valid: true}, - UnitTypeId: sql.NullInt64{Int64: unit_type.Id, Valid: true}, + NumValue: models.NullFloat64{sql.NullFloat64{Float64: 1.23, Valid: true}}, + UnitTypeId: models.NullInt64{sql.NullInt64{Int64: unit_type.Id, Valid: true}}, } } @@ -108,7 +108,7 @@ func TestMeasurementsStore_Update_db(t *testing.T) { d := NewDatastore(tx) // Tweak it - measurement.NumValue = sql.NullFloat64{Float64: 4.56, Valid: true} + measurement.NumValue = models.NullFloat64{sql.NullFloat64{Float64: 4.56, Valid: true}} updated, err := d.Measurements.Update(measurement.Id, measurement) if err != nil { t.Fatal(err) diff --git a/models/client_test.go b/models/client_test.go index 6ad7cbc..3aebdb4 100644 --- a/models/client_test.go +++ b/models/client_test.go @@ -99,9 +99,9 @@ func normalizeTime(t ...interface{}) { case *time.Time: x, _ := v.(*time.Time) *x = x.In(time.UTC) - case *pq.NullTime: - x, _ := v.(*pq.NullTime) - *x = pq.NullTime{Time: x.Time.In(time.UTC), Valid: x.Valid} + case *NullTime: + x, _ := v.(*NullTime) + *x = NullTime{pq.NullTime{Time: x.Time.In(time.UTC), Valid: x.Valid}} } } } diff --git a/models/genera.go b/models/genera.go index 71dbfdf..6bbd614 100644 --- a/models/genera.go +++ b/models/genera.go @@ -6,17 +6,16 @@ import ( "strconv" "time" - "github.com/lib/pq" "github.com/thermokarst/bactdb/router" ) // A Genus is a high-level classifier in bactdb. type Genus struct { - Id int64 `json:"id,omitempty"` - GenusName string `db:"genus_name" json:"genusName"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` - DeletedAt pq.NullTime `db:"deleted_at" json:"deletedAt"` + Id int64 `json:"id,omitempty"` + GenusName string `db:"genus_name" json:"genusName"` + CreatedAt time.Time `db:"created_at" json:"createdAt"` + UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` + DeletedAt NullTime `db:"deleted_at" json:"deletedAt"` } func NewGenus() *Genus { diff --git a/models/genera_test.go b/models/genera_test.go index 2555430..806da27 100644 --- a/models/genera_test.go +++ b/models/genera_test.go @@ -54,7 +54,7 @@ func TestGeneraService_Create(t *testing.T) { mux.HandleFunc(urlPath(t, router.CreateGenus, nil), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "POST") - testBody(t, r, `{"id":1,"genusName":"Test Genus","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"genusName":"Test Genus","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusCreated) writeJSON(w, want) @@ -124,7 +124,7 @@ func TestGeneraService_Update(t *testing.T) { mux.HandleFunc(urlPath(t, router.UpdateGenus, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "PUT") - testBody(t, r, `{"id":1,"genusName":"Test Genus Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"genusName":"Test Genus Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusOK) writeJSON(w, want) diff --git a/models/measurements.go b/models/measurements.go index f5f0bdd..f643e2f 100644 --- a/models/measurements.go +++ b/models/measurements.go @@ -15,23 +15,23 @@ import ( // has a constraint that will allow one or the other for a particular // combination of strain & observation, but not both. type Measurement struct { - Id int64 `json:"id,omitempty"` - StrainId int64 `db:"strain_id" json:"strainId"` - ObservationId int64 `db:"observation_id" json:"observationId"` - TextMeasurementTypeId sql.NullInt64 `db:"text_measurement_type_id" json:"textMeasurementTypeId"` - TxtValue sql.NullString `db:"txt_value" json:"txtValue"` - NumValue sql.NullFloat64 `db:"num_value" json:"numValue"` - ConfidenceInterval sql.NullFloat64 `db:"confidence_interval" json:"confidenceInterval"` - UnitTypeId sql.NullInt64 `db:"unit_type_id" json:"unitTypeId"` - Notes sql.NullString `db:"notes" json:"notes"` - TestMethodId sql.NullInt64 `db:"test_method_id" json:"testMethodId"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` + Id int64 `json:"id,omitempty"` + StrainId int64 `db:"strain_id" json:"strainId"` + ObservationId int64 `db:"observation_id" json:"observationId"` + TextMeasurementTypeId NullInt64 `db:"text_measurement_type_id" json:"textMeasurementTypeId"` + TxtValue NullString `db:"txt_value" json:"txtValue"` + NumValue NullFloat64 `db:"num_value" json:"numValue"` + ConfidenceInterval NullFloat64 `db:"confidence_interval" json:"confidenceInterval"` + UnitTypeId NullInt64 `db:"unit_type_id" json:"unitTypeId"` + Notes NullString `db:"notes" json:"notes"` + TestMethodId NullInt64 `db:"test_method_id" json:"testMethodId"` + CreatedAt time.Time `db:"created_at" json:"createdAt"` + UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` } func NewMeasurement() *Measurement { return &Measurement{ - NumValue: sql.NullFloat64{Float64: 1.23, Valid: true}, + NumValue: NullFloat64{sql.NullFloat64{Float64: 1.23, Valid: true}}, } } diff --git a/models/measurements_test.go b/models/measurements_test.go index 81a3713..55785b1 100644 --- a/models/measurements_test.go +++ b/models/measurements_test.go @@ -14,7 +14,7 @@ func newMeasurement() *Measurement { measurement.Id = 1 measurement.StrainId = 1 measurement.ObservationId = 1 - measurement.UnitTypeId = sql.NullInt64{Int64: 1, Valid: true} + measurement.UnitTypeId = NullInt64{sql.NullInt64{Int64: 1, Valid: true}} return measurement } @@ -58,7 +58,7 @@ func TestMeasurementService_Create(t *testing.T) { mux.HandleFunc(urlPath(t, router.CreateMeasurement, nil), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "POST") - testBody(t, r, `{"id":1,"strainId":1,"observationId":1,"textMeasurementTypeId":{"Int64":0,"Valid":false},"txtValue":{"String":"","Valid":false},"numValue":{"Float64":1.23,"Valid":true},"confidenceInterval":{"Float64":0,"Valid":false},"unitTypeId":{"Int64":1,"Valid":true},"notes":{"String":"","Valid":false},"testMethodId":{"Int64":0,"Valid":false},"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}`+"\n") + testBody(t, r, `{"id":1,"strainId":1,"observationId":1,"textMeasurementTypeId":null,"txtValue":null,"numValue":1.23,"confidenceInterval":null,"unitTypeId":1,"notes":null,"testMethodId":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}`+"\n") w.WriteHeader(http.StatusCreated) writeJSON(w, want) @@ -127,13 +127,13 @@ func TestMeasurementService_Update(t *testing.T) { 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},"txtValue":{"String":"","Valid":false},"numValue":{"Float64":4.56,"Valid":true},"confidenceInterval":{"Float64":0,"Valid":false},"unitTypeId":{"Int64":1,"Valid":true},"notes":{"String":"","Valid":false},"testMethodId":{"Int64":0,"Valid":false},"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}`+"\n") + testBody(t, r, `{"id":1,"strainId":1,"observationId":1,"textMeasurementTypeId":null,"txtValue":null,"numValue":4.56,"confidenceInterval":null,"unitTypeId":1,"notes":null,"testMethodId":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}`+"\n") w.WriteHeader(http.StatusOK) writeJSON(w, want) }) measurement := newMeasurement() - measurement.NumValue = sql.NullFloat64{Float64: 4.56, Valid: true} + measurement.NumValue = NullFloat64{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) diff --git a/models/observation_types.go b/models/observation_types.go index beaf41d..20aaa3b 100644 --- a/models/observation_types.go +++ b/models/observation_types.go @@ -6,17 +6,16 @@ import ( "strconv" "time" - "github.com/lib/pq" "github.com/thermokarst/bactdb/router" ) // An Observation Type is a lookup type type ObservationType struct { - Id int64 `json:"id,omitempty"` - ObservationTypeName string `db:"observation_type_name" json:"observationTypeName"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` - DeletedAt pq.NullTime `db:"deleted_at" json:"deletedAt"` + Id int64 `json:"id,omitempty"` + ObservationTypeName string `db:"observation_type_name" json:"observationTypeName"` + CreatedAt time.Time `db:"created_at" json:"createdAt"` + UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` + DeletedAt NullTime `db:"deleted_at" json:"deletedAt"` } func NewObservationType() *ObservationType { diff --git a/models/observation_types_test.go b/models/observation_types_test.go index 6718ff7..74fcbad 100644 --- a/models/observation_types_test.go +++ b/models/observation_types_test.go @@ -54,7 +54,7 @@ func TestObservationTypeService_Create(t *testing.T) { mux.HandleFunc(urlPath(t, router.CreateObservationType, nil), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "POST") - testBody(t, r, `{"id":1,"observationTypeName":"Test Obs Type","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"observationTypeName":"Test Obs Type","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusCreated) writeJSON(w, want) @@ -123,7 +123,7 @@ func TestObservationTypeService_Update(t *testing.T) { mux.HandleFunc(urlPath(t, router.UpdateObservationType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "PUT") - testBody(t, r, `{"id":1,"observationTypeName":"Test Obs Type Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"observationTypeName":"Test Obs Type Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusOK) writeJSON(w, want) }) diff --git a/models/observations.go b/models/observations.go index 8596e04..e11dd2c 100644 --- a/models/observations.go +++ b/models/observations.go @@ -6,18 +6,17 @@ import ( "strconv" "time" - "github.com/lib/pq" "github.com/thermokarst/bactdb/router" ) // An Observation is a lookup type type Observation struct { - Id int64 `json:"id,omitempty"` - ObservationName string `db:"observation_name" json:"observationName"` - ObservationTypeId int64 `db:"observation_type_id" json:"observationTypeId"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` - DeletedAt pq.NullTime `db:"deleted_at" json:"deletedAt"` + Id int64 `json:"id,omitempty"` + ObservationName string `db:"observation_name" json:"observationName"` + ObservationTypeId int64 `db:"observation_type_id" json:"observationTypeId"` + CreatedAt time.Time `db:"created_at" json:"createdAt"` + UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` + DeletedAt NullTime `db:"deleted_at" json:"deletedAt"` } func NewObservation() *Observation { diff --git a/models/observations_test.go b/models/observations_test.go index 0267552..5fa88c6 100644 --- a/models/observations_test.go +++ b/models/observations_test.go @@ -54,7 +54,7 @@ func TestObservationService_Create(t *testing.T) { 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,"observationName":"Test Observation","observationTypeId":0,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"observationName":"Test Observation","observationTypeId":0,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusCreated) writeJSON(w, want) @@ -123,7 +123,7 @@ func TestObservationService_Update(t *testing.T) { mux.HandleFunc(urlPath(t, router.UpdateObservation, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "PUT") - testBody(t, r, `{"id":1,"observationName":"Test Obs Updated","observationTypeId":0,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"observationName":"Test Obs Updated","observationTypeId":0,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusOK) writeJSON(w, want) }) diff --git a/models/species.go b/models/species.go index ad3375d..957eaa5 100644 --- a/models/species.go +++ b/models/species.go @@ -6,18 +6,17 @@ import ( "strconv" "time" - "github.com/lib/pq" "github.com/thermokarst/bactdb/router" ) // A Species is a high-level classifier in bactdb. type Species struct { - Id int64 `json:"id,omitempty"` - GenusId int64 `db:"genus_id" json:"genusId"` - SpeciesName string `db:"species_name" json:"speciesName"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` - DeletedAt pq.NullTime `db:"deleted_at" json:"deletedAt"` + Id int64 `json:"id,omitempty"` + GenusId int64 `db:"genus_id" json:"genusId"` + SpeciesName string `db:"species_name" json:"speciesName"` + CreatedAt time.Time `db:"created_at" json:"createdAt"` + UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` + DeletedAt NullTime `db:"deleted_at" json:"deletedAt"` } func NewSpecies() *Species { diff --git a/models/species_test.go b/models/species_test.go index 2405d50..ad53f48 100644 --- a/models/species_test.go +++ b/models/species_test.go @@ -55,7 +55,7 @@ func TestSpeciesService_Create(t *testing.T) { mux.HandleFunc(urlPath(t, router.CreateSpecies, nil), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "POST") - testBody(t, r, `{"id":1,"genusId":1,"speciesName":"Test Species","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"genusId":1,"speciesName":"Test Species","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusCreated) writeJSON(w, want) @@ -124,7 +124,7 @@ func TestSpeciesService_Update(t *testing.T) { mux.HandleFunc(urlPath(t, router.UpdateSpecies, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "PUT") - testBody(t, r, `{"id":1,"genusId":1,"speciesName":"Test Species Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"genusId":1,"speciesName":"Test Species Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusOK) writeJSON(w, want) diff --git a/models/strains.go b/models/strains.go index bd5e8c4..b9b40fb 100644 --- a/models/strains.go +++ b/models/strains.go @@ -1,29 +1,27 @@ package models import ( - "database/sql" "errors" "net/http" "strconv" "time" - "github.com/lib/pq" "github.com/thermokarst/bactdb/router" ) // A Strain is a subclass of species type Strain struct { - Id int64 `json:"id,omitempty"` - SpeciesId int64 `db:"species_id" json:"speciesId"` - StrainName string `db:"strain_name" json:"strainName"` - StrainType string `db:"strain_type" json:"strainType"` - Etymology string `db:"etymology" json:"etymology"` - AccessionBanks string `db:"accession_banks" json:"accessionBanks"` - GenbankEmblDdb string `db:"genbank_embl_ddb" json:"genbankEmblDdb"` - IsolatedFrom sql.NullString `db:"isolated_from" json:"isolatedFrom"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` - DeletedAt pq.NullTime `db:"deleted_at" json:"deletedAt"` + Id int64 `json:"id,omitempty"` + SpeciesId int64 `db:"species_id" json:"speciesId"` + StrainName string `db:"strain_name" json:"strainName"` + StrainType string `db:"strain_type" json:"strainType"` + Etymology string `db:"etymology" json:"etymology"` + AccessionBanks string `db:"accession_banks" json:"accessionBanks"` + GenbankEmblDdb string `db:"genbank_embl_ddb" json:"genbankEmblDdb"` + IsolatedFrom NullString `db:"isolated_from" json:"isolatedFrom"` + CreatedAt time.Time `db:"created_at" json:"createdAt"` + UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` + DeletedAt NullTime `db:"deleted_at" json:"deletedAt"` } func NewStrain() *Strain { diff --git a/models/strains_test.go b/models/strains_test.go index c9fb9fe..2ab291f 100644 --- a/models/strains_test.go +++ b/models/strains_test.go @@ -55,7 +55,7 @@ func TestStrainService_Create(t *testing.T) { mux.HandleFunc(urlPath(t, router.CreateStrain, nil), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "POST") - testBody(t, r, `{"id":1,"speciesId":1,"strainName":"Test Strain","strainType":"Test Type","etymology":"Test Etymology","accessionBanks":"Test Accession","genbankEmblDdb":"Test Genbank","isolatedFrom":{"String":"","Valid":false},"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"speciesId":1,"strainName":"Test Strain","strainType":"Test Type","etymology":"Test Etymology","accessionBanks":"Test Accession","genbankEmblDdb":"Test Genbank","isolatedFrom":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusCreated) writeJSON(w, want) @@ -124,7 +124,7 @@ func TestStrainService_Update(t *testing.T) { mux.HandleFunc(urlPath(t, router.UpdateStrain, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "PUT") - testBody(t, r, `{"id":1,"speciesId":1,"strainName":"Test Strain Updated","strainType":"Test Type Updated","etymology":"Test Etymology Updated","accessionBanks":"Test Accession Updated","genbankEmblDdb":"Test Genbank Updated","isolatedFrom":{"String":"","Valid":false},"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"speciesId":1,"strainName":"Test Strain Updated","strainType":"Test Type Updated","etymology":"Test Etymology Updated","accessionBanks":"Test Accession Updated","genbankEmblDdb":"Test Genbank Updated","isolatedFrom":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusOK) writeJSON(w, want) }) diff --git a/models/text_measurement_types.go b/models/text_measurement_types.go index 05fdf43..85a4ffd 100644 --- a/models/text_measurement_types.go +++ b/models/text_measurement_types.go @@ -6,17 +6,16 @@ import ( "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"` + 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 NullTime `db:"deleted_at" json:"deletedAt"` } func NewTextMeasurementType() *TextMeasurementType { diff --git a/models/text_measurement_types_test.go b/models/text_measurement_types_test.go index b77cfa2..fe958c4 100644 --- a/models/text_measurement_types_test.go +++ b/models/text_measurement_types_test.go @@ -54,7 +54,7 @@ func TestTextMeasurementTypeService_Create(t *testing.T) { mux.HandleFunc(urlPath(t, router.CreateTextMeasurementType, nil), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "POST") - testBody(t, r, `{"id":1,"textMeasurementName":"Test Text Measurement Type","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"textMeasurementName":"Test Text Measurement Type","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusCreated) writeJSON(w, want) @@ -123,7 +123,7 @@ func TestTextMeasurementTypeService_Update(t *testing.T) { mux.HandleFunc(urlPath(t, router.UpdateTextMeasurementType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "PUT") - testBody(t, r, `{"id":1,"textMeasurementName":"Test Text Measurement Type Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"textMeasurementName":"Test Text Measurement Type Updated","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusOK) writeJSON(w, want) }) diff --git a/models/types.go b/models/types.go new file mode 100644 index 0000000..87eb22e --- /dev/null +++ b/models/types.go @@ -0,0 +1,131 @@ +package models + +import ( + "bytes" + "database/sql" + "encoding/json" + "time" + + "github.com/lib/pq" +) + +type NullString struct { + sql.NullString +} + +func (s *NullString) MarshalJSON() ([]byte, error) { + if !s.Valid { + return []byte("null"), nil + } + return json.Marshal(s.String) +} + +func (s *NullString) UnmarshalJSON(b []byte) error { + if bytes.Equal(b, []byte("null")) { + s.String = "" + s.Valid = false + return nil + } + var x interface{} + var err error + json.Unmarshal(b, &x) + switch x.(type) { + case float64: + err = json.Unmarshal(b, &s.String) + case map[string]interface{}: + err = json.Unmarshal(b, &s.NullString) + } + s.Valid = true + return err +} + +type NullInt64 struct { + sql.NullInt64 +} + +func (i *NullInt64) MarshalJSON() ([]byte, error) { + if !i.Valid { + return []byte("null"), nil + } + return json.Marshal(i.Int64) +} + +func (i *NullInt64) UnmarshalJSON(b []byte) error { + if bytes.Equal(b, []byte("null")) { + i.Int64 = 0 + i.Valid = false + return nil + } + var x interface{} + var err error + json.Unmarshal(b, &x) + switch x.(type) { + case float64: + err = json.Unmarshal(b, &i.Int64) + case map[string]interface{}: + err = json.Unmarshal(b, &i.NullInt64) + } + i.Valid = true + return err +} + +type NullFloat64 struct { + sql.NullFloat64 +} + +func (f *NullFloat64) MarshalJSON() ([]byte, error) { + if !f.Valid { + return []byte("null"), nil + } + return json.Marshal(f.Float64) +} + +func (f *NullFloat64) UnmarshalJSON(b []byte) error { + if bytes.Equal(b, []byte("null")) { + f.Float64 = 0 + f.Valid = false + return nil + } + var x interface{} + var err error + json.Unmarshal(b, &x) + switch x.(type) { + case float64: + err = json.Unmarshal(b, &f.Float64) + case map[string]interface{}: + err = json.Unmarshal(b, &f.NullFloat64) + } + f.Valid = true + return err +} + +type NullTime struct { + pq.NullTime +} + +func (t *NullTime) MarshalJSON() ([]byte, error) { + if !t.Valid { + return []byte("null"), nil + } + return json.Marshal(t.Time) +} + +func (t *NullTime) UnmarshalJSON(b []byte) error { + if bytes.Equal(b, []byte("null")) { + var nt time.Time + t.Time = nt.In(time.UTC) + t.Valid = false + return nil + } + var x interface{} + var err error + json.Unmarshal(b, &x) + switch x.(type) { + case time.Time: + err = json.Unmarshal(b, &t.Time) + case map[string]interface{}: + err = json.Unmarshal(b, &t.NullTime) + } + t.Valid = true + return err +} diff --git a/models/unit_types.go b/models/unit_types.go index f1ca92b..841fc26 100644 --- a/models/unit_types.go +++ b/models/unit_types.go @@ -6,18 +6,17 @@ import ( "strconv" "time" - "github.com/lib/pq" "github.com/thermokarst/bactdb/router" ) // A UnitType is a lookup type type UnitType struct { - Id int64 `json:"id,omitempty"` - Name string `db:"name" json:"name"` - Symbol string `db:"symbol" json:"symbol"` - CreatedAt time.Time `db:"created_at" json:"createdAt"` - UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` - DeletedAt pq.NullTime `db:"deleted_at" json:"deletedAt"` + Id int64 `json:"id,omitempty"` + Name string `db:"name" json:"name"` + Symbol string `db:"symbol" json:"symbol"` + CreatedAt time.Time `db:"created_at" json:"createdAt"` + UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` + DeletedAt NullTime `db:"deleted_at" json:"deletedAt"` } func NewUnitType() *UnitType { diff --git a/models/unit_types_test.go b/models/unit_types_test.go index 968b240..c08e0b2 100644 --- a/models/unit_types_test.go +++ b/models/unit_types_test.go @@ -54,7 +54,7 @@ func TestUnitTypeService_Create(t *testing.T) { mux.HandleFunc(urlPath(t, router.CreateUnitType, nil), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "POST") - testBody(t, r, `{"id":1,"name":"Test Unit Type","symbol":"x","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"name":"Test Unit Type","symbol":"x","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusCreated) writeJSON(w, want) @@ -123,7 +123,7 @@ func TestUnitTypeService_Update(t *testing.T) { mux.HandleFunc(urlPath(t, router.UpdateUnitType, map[string]string{"Id": "1"}), func(w http.ResponseWriter, r *http.Request) { called = true testMethod(t, r, "PUT") - testBody(t, r, `{"id":1,"name":"Test Unit Type Updated","symbol":"x","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false}}`+"\n") + testBody(t, r, `{"id":1,"name":"Test Unit Type Updated","symbol":"x","createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z","deletedAt":null}`+"\n") w.WriteHeader(http.StatusOK) writeJSON(w, want) })