From 8a968645ecbd8e40bcf57a584b4c79f586d0f0a7 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Sun, 2 Apr 2023 14:34:32 -0700 Subject: [PATCH 01/15] add new query params to execute paginated api calls to the server --- api/handlers/api.go | 2 +- api/handlers/call.go | 14 ++++++++- api/handlers/civilian.go | 15 +++++++++- api/handlers/ems.go | 15 +++++++++- api/handlers/emsVehicle.go | 15 +++++++++- api/handlers/firearm.go | 22 ++++++++++++++- api/handlers/vehicle.go | 15 +++++++++- databases/call.go | 13 +++++---- databases/civilian.go | 15 +++++----- databases/database.go | 12 ++++---- databases/ems.go | 15 +++++----- databases/emsvehicle.go | 15 +++++----- databases/firearm.go | 13 +++++---- databases/mocks/CollectionHelper.go | 36 ++++++++++++++++------- databases/mocks/FirearmDatabase.go | 44 ++++++++++++++++++++--------- databases/utility.go | 23 +++++++++++++++ databases/vehicle.go | 15 +++++----- 17 files changed, 222 insertions(+), 77 deletions(-) create mode 100644 databases/utility.go diff --git a/api/handlers/api.go b/api/handlers/api.go index 25e3446..66939e6 100644 --- a/api/handlers/api.go +++ b/api/handlers/api.go @@ -57,7 +57,7 @@ func (a *App) New() *mux.Router { apiCreate.Handle("/vehicles", api.Middleware(http.HandlerFunc(v.VehicleHandler))).Methods("GET") apiCreate.Handle("/vehicles/user/{user_id}", api.Middleware(http.HandlerFunc(v.VehiclesByUserIDHandler))).Methods("GET") apiCreate.Handle("/firearm/{firearm_id}", api.Middleware(http.HandlerFunc(f.FirearmByIDHandler))).Methods("GET") - apiCreate.Handle("/firearms", api.Middleware(http.HandlerFunc(f.FirearmHandler))).Methods("GET") + apiCreate.Handle("/firearms", api.Middleware(api.Pagination(http.HandlerFunc(f.FirearmHandler)))).Methods("GET") apiCreate.Handle("/firearms/user/{user_id}", api.Middleware(http.HandlerFunc(f.FirearmsByUserIDHandler))).Methods("GET") apiCreate.Handle("/ems/{ems_id}", api.Middleware(http.HandlerFunc(e.EmsByIDHandler))).Methods("GET") apiCreate.Handle("/ems", api.Middleware(http.HandlerFunc(e.EmsHandler))).Methods("GET") diff --git a/api/handlers/call.go b/api/handlers/call.go index 51300a8..32ef54c 100644 --- a/api/handlers/call.go +++ b/api/handlers/call.go @@ -3,12 +3,14 @@ package handlers import ( "context" "encoding/json" + "fmt" "net/http" "strconv" "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" "go.uber.org/zap" "github.com/linesmerrill/police-cad-api/config" @@ -23,7 +25,17 @@ type Call struct { // CallHandler returns all calls func (c Call) CallHandler(w http.ResponseWriter, r *http.Request) { - dbResp, err := c.DB.Find(context.TODO(), bson.M{}) + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) + } + limit64 := int64(Limit) + Page, err := strconv.Atoi(r.URL.Query().Get("page")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) + } + skip64 := int64(Page) + dbResp, err := c.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get calls", http.StatusNotFound, w, err) return diff --git a/api/handlers/civilian.go b/api/handlers/civilian.go index 0244e8d..7577c8a 100644 --- a/api/handlers/civilian.go +++ b/api/handlers/civilian.go @@ -3,11 +3,14 @@ package handlers import ( "context" "encoding/json" + "fmt" "net/http" + "strconv" "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" "go.uber.org/zap" "github.com/linesmerrill/police-cad-api/config" @@ -22,7 +25,17 @@ type Civilian struct { // CivilianHandler returns all civilians func (c Civilian) CivilianHandler(w http.ResponseWriter, r *http.Request) { - dbResp, err := c.DB.Find(context.TODO(), bson.M{}) + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) + } + limit64 := int64(Limit) + Page, err := strconv.Atoi(r.URL.Query().Get("page")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) + } + skip64 := int64(Page) + dbResp, err := c.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get civilians", http.StatusNotFound, w, err) return diff --git a/api/handlers/ems.go b/api/handlers/ems.go index b430676..a5fefbe 100644 --- a/api/handlers/ems.go +++ b/api/handlers/ems.go @@ -3,11 +3,14 @@ package handlers import ( "context" "encoding/json" + "fmt" "net/http" + "strconv" "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" "go.uber.org/zap" "github.com/linesmerrill/police-cad-api/config" @@ -22,7 +25,17 @@ type Ems struct { // EmsHandler returns all ems func (e Ems) EmsHandler(w http.ResponseWriter, r *http.Request) { - dbResp, err := e.DB.Find(context.TODO(), bson.M{}) + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) + } + limit64 := int64(Limit) + Page, err := strconv.Atoi(r.URL.Query().Get("page")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) + } + skip64 := int64(Page) + dbResp, err := e.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get ems", http.StatusNotFound, w, err) return diff --git a/api/handlers/emsVehicle.go b/api/handlers/emsVehicle.go index 686beb9..89f9ff0 100644 --- a/api/handlers/emsVehicle.go +++ b/api/handlers/emsVehicle.go @@ -3,11 +3,14 @@ package handlers import ( "context" "encoding/json" + "fmt" "net/http" + "strconv" "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" "go.uber.org/zap" "github.com/linesmerrill/police-cad-api/config" @@ -22,7 +25,17 @@ type EmsVehicle struct { // EmsVehicleHandler returns all emsVehicles func (v EmsVehicle) EmsVehicleHandler(w http.ResponseWriter, r *http.Request) { - dbResp, err := v.DB.Find(context.TODO(), bson.M{}) + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) + } + limit64 := int64(Limit) + Page, err := strconv.Atoi(r.URL.Query().Get("page")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) + } + skip64 := int64(Page) + dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get emsVehicles", http.StatusNotFound, w, err) return diff --git a/api/handlers/firearm.go b/api/handlers/firearm.go index 19836ea..f4d6bc4 100644 --- a/api/handlers/firearm.go +++ b/api/handlers/firearm.go @@ -3,11 +3,14 @@ package handlers import ( "context" "encoding/json" + "fmt" "net/http" + "strconv" "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" "go.uber.org/zap" "github.com/linesmerrill/police-cad-api/config" @@ -20,13 +23,30 @@ type Firearm struct { DB databases.FirearmDatabase } +// FirearmList paginated response with a list of items and next page id +type FirearmList struct { + Items []*models.Firearm `json:"items"` + NextPageID int `json:"next_page_id,omitempty" example:"10"` +} + // FirearmHandler returns all firearms func (v Firearm) FirearmHandler(w http.ResponseWriter, r *http.Request) { - dbResp, err := v.DB.Find(context.TODO(), bson.M{}) + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) + } + limit64 := int64(Limit) + Page, err := strconv.Atoi(r.URL.Query().Get("page")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) + } + skip64 := int64(Page) + dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get firearms", http.StatusNotFound, w, err) return } + // Because the frontend requires that the data elements inside models.Firearms exist, if // len == 0 then we will just return an empty data object if len(dbResp) == 0 { diff --git a/api/handlers/vehicle.go b/api/handlers/vehicle.go index 11e33b7..5bcd7d8 100644 --- a/api/handlers/vehicle.go +++ b/api/handlers/vehicle.go @@ -3,11 +3,14 @@ package handlers import ( "context" "encoding/json" + "fmt" "net/http" + "strconv" "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" "go.uber.org/zap" "github.com/linesmerrill/police-cad-api/config" @@ -22,7 +25,17 @@ type Vehicle struct { // VehicleHandler returns all vehicles func (v Vehicle) VehicleHandler(w http.ResponseWriter, r *http.Request) { - dbResp, err := v.DB.Find(context.TODO(), bson.M{}) + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) + } + limit64 := int64(Limit) + Page, err := strconv.Atoi(r.URL.Query().Get("page")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) + } + skip64 := int64(Page) + dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get vehicles", http.StatusNotFound, w, err) return diff --git a/databases/call.go b/databases/call.go index fb895f0..e8b9e58 100644 --- a/databases/call.go +++ b/databases/call.go @@ -6,14 +6,15 @@ import ( "context" "github.com/linesmerrill/police-cad-api/models" + "go.mongodb.org/mongo-driver/mongo/options" ) const callName = "calls" // CallDatabase contains the methods to use with the call database type CallDatabase interface { - FindOne(ctx context.Context, filter interface{}) (*models.Call, error) - Find(ctx context.Context, filter interface{}) ([]models.Call, error) + FindOne(context.Context, interface{}, ...*options.FindOneOptions) (*models.Call, error) + Find(context.Context, interface{}, ...*options.FindOptions) ([]models.Call, error) } type callDatabase struct { @@ -27,18 +28,18 @@ func NewCallDatabase(db DatabaseHelper) CallDatabase { } } -func (c *callDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Call, error) { +func (c *callDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.Call, error) { call := &models.Call{} - err := c.db.Collection(callName).FindOne(ctx, filter).Decode(&call) + err := c.db.Collection(callName).FindOne(ctx, filter, opts...).Decode(&call) if err != nil { return nil, err } return call, nil } -func (c *callDatabase) Find(ctx context.Context, filter interface{}) ([]models.Call, error) { +func (c *callDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.Call, error) { var calls []models.Call - err := c.db.Collection(callName).Find(ctx, filter).Decode(&calls) + err := c.db.Collection(callName).Find(ctx, filter, opts...).Decode(&calls) if err != nil { return nil, err } diff --git a/databases/civilian.go b/databases/civilian.go index 2ea4ad3..959c026 100644 --- a/databases/civilian.go +++ b/databases/civilian.go @@ -1,19 +1,20 @@ package databases -//go generate: mockery --name CivilianDatabase +// go generate: mockery --name CivilianDatabase import ( "context" "github.com/linesmerrill/police-cad-api/models" + "go.mongodb.org/mongo-driver/mongo/options" ) const civilianName = "civilians" // CivilianDatabase contains the methods to use with the civilian database type CivilianDatabase interface { - FindOne(ctx context.Context, filter interface{}) (*models.Civilian, error) - Find(ctx context.Context, filter interface{}) ([]models.Civilian, error) + FindOne(context.Context, interface{}, ...*options.FindOneOptions) (*models.Civilian, error) + Find(context.Context, interface{}, ...*options.FindOptions) ([]models.Civilian, error) } type civilianDatabase struct { @@ -27,18 +28,18 @@ func NewCivilianDatabase(db DatabaseHelper) CivilianDatabase { } } -func (c *civilianDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Civilian, error) { +func (c *civilianDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.Civilian, error) { civilian := &models.Civilian{} - err := c.db.Collection(civilianName).FindOne(ctx, filter).Decode(&civilian) + err := c.db.Collection(civilianName).FindOne(ctx, filter, opts...).Decode(&civilian) if err != nil { return nil, err } return civilian, nil } -func (c *civilianDatabase) Find(ctx context.Context, filter interface{}) ([]models.Civilian, error) { +func (c *civilianDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.Civilian, error) { var civilians []models.Civilian - err := c.db.Collection(civilianName).Find(ctx, filter).Decode(&civilians) + err := c.db.Collection(civilianName).Find(ctx, filter, opts...).Decode(&civilians) if err != nil { return nil, err } diff --git a/databases/database.go b/databases/database.go index 8f0fab7..cf23963 100644 --- a/databases/database.go +++ b/databases/database.go @@ -18,8 +18,8 @@ type DatabaseHelper interface { // CollectionHelper contains all the methods defined for collections in this project type CollectionHelper interface { - FindOne(context.Context, interface{}) SingleResultHelper - Find(context.Context, interface{}) CursorHelper + FindOne(context.Context, interface{}, ...*options.FindOneOptions) SingleResultHelper + Find(context.Context, interface{}, ...*options.FindOptions) CursorHelper } // SingleResultHelper contains a single method to decode the result @@ -99,13 +99,13 @@ func (md *mongoDatabase) Client() ClientHelper { return &mongoClient{cl: client} } -func (mc *mongoCollection) FindOne(ctx context.Context, filter interface{}) SingleResultHelper { - singleResult := mc.coll.FindOne(ctx, filter) +func (mc *mongoCollection) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) SingleResultHelper { + singleResult := mc.coll.FindOne(ctx, filter, opts...) return &mongoSingleResult{sr: singleResult} } -func (mc *mongoCollection) Find(ctx context.Context, filter interface{}) CursorHelper { - cursor, _ := mc.coll.Find(ctx, filter) +func (mc *mongoCollection) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) CursorHelper { + cursor, _ := mc.coll.Find(ctx, filter, opts...) return &mongoCursor{cr: cursor} } diff --git a/databases/ems.go b/databases/ems.go index cc22fe8..ea8bf1f 100644 --- a/databases/ems.go +++ b/databases/ems.go @@ -1,19 +1,20 @@ package databases -//go generate: mockery --name EmsDatabase +// go generate: mockery --name EmsDatabase import ( "context" "github.com/linesmerrill/police-cad-api/models" + "go.mongodb.org/mongo-driver/mongo/options" ) const emsName = "ems" // EmsDatabase contains the methods to use with the ems database type EmsDatabase interface { - FindOne(ctx context.Context, filter interface{}) (*models.Ems, error) - Find(ctx context.Context, filter interface{}) ([]models.Ems, error) + FindOne(context.Context, interface{}, ...*options.FindOneOptions) (*models.Ems, error) + Find(context.Context, interface{}, ...*options.FindOptions) ([]models.Ems, error) } type emsDatabase struct { @@ -27,18 +28,18 @@ func NewEmsDatabase(db DatabaseHelper) EmsDatabase { } } -func (c *emsDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Ems, error) { +func (c *emsDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.Ems, error) { ems := &models.Ems{} - err := c.db.Collection(emsName).FindOne(ctx, filter).Decode(&ems) + err := c.db.Collection(emsName).FindOne(ctx, filter, opts...).Decode(&ems) if err != nil { return nil, err } return ems, nil } -func (c *emsDatabase) Find(ctx context.Context, filter interface{}) ([]models.Ems, error) { +func (c *emsDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.Ems, error) { var ems []models.Ems - err := c.db.Collection(emsName).Find(ctx, filter).Decode(&ems) + err := c.db.Collection(emsName).Find(ctx, filter, opts...).Decode(&ems) if err != nil { return nil, err } diff --git a/databases/emsvehicle.go b/databases/emsvehicle.go index 06ece24..e5777a3 100644 --- a/databases/emsvehicle.go +++ b/databases/emsvehicle.go @@ -1,19 +1,20 @@ package databases -//go generate: mockery --name EmsVehicleDatabase +// go generate: mockery --name EmsVehicleDatabase import ( "context" "github.com/linesmerrill/police-cad-api/models" + "go.mongodb.org/mongo-driver/mongo/options" ) const emsVehicleName = "emsvehicles" // EmsVehicleDatabase contains the methods to use with the emsVehicle database type EmsVehicleDatabase interface { - FindOne(ctx context.Context, filter interface{}) (*models.EmsVehicle, error) - Find(ctx context.Context, filter interface{}) ([]models.EmsVehicle, error) + FindOne(context.Context, interface{}, ...*options.FindOneOptions) (*models.EmsVehicle, error) + Find(context.Context, interface{}, ...*options.FindOptions) ([]models.EmsVehicle, error) } type emsVehicleDatabase struct { @@ -27,18 +28,18 @@ func NewEmsVehicleDatabase(db DatabaseHelper) EmsVehicleDatabase { } } -func (c *emsVehicleDatabase) FindOne(ctx context.Context, filter interface{}) (*models.EmsVehicle, error) { +func (c *emsVehicleDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.EmsVehicle, error) { emsVehicle := &models.EmsVehicle{} - err := c.db.Collection(emsVehicleName).FindOne(ctx, filter).Decode(&emsVehicle) + err := c.db.Collection(emsVehicleName).FindOne(ctx, filter, opts...).Decode(&emsVehicle) if err != nil { return nil, err } return emsVehicle, nil } -func (c *emsVehicleDatabase) Find(ctx context.Context, filter interface{}) ([]models.EmsVehicle, error) { +func (c *emsVehicleDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.EmsVehicle, error) { var emsVehicles []models.EmsVehicle - err := c.db.Collection(emsVehicleName).Find(ctx, filter).Decode(&emsVehicles) + err := c.db.Collection(emsVehicleName).Find(ctx, filter, opts...).Decode(&emsVehicles) if err != nil { return nil, err } diff --git a/databases/firearm.go b/databases/firearm.go index 77eb0fe..5edae55 100644 --- a/databases/firearm.go +++ b/databases/firearm.go @@ -1,19 +1,20 @@ package databases -//go generate: mockery --name FirearmDatabase +// go generate: mockery --name FirearmDatabase import ( "context" "github.com/linesmerrill/police-cad-api/models" + "go.mongodb.org/mongo-driver/mongo/options" ) const firearmName = "firearms" // FirearmDatabase contains the methods to use with the firearm database type FirearmDatabase interface { - FindOne(ctx context.Context, filter interface{}) (*models.Firearm, error) - Find(ctx context.Context, filter interface{}) ([]models.Firearm, error) + FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.Firearm, error) + Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.Firearm, error) } type firearmDatabase struct { @@ -27,7 +28,7 @@ func NewFirearmDatabase(db DatabaseHelper) FirearmDatabase { } } -func (c *firearmDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Firearm, error) { +func (c *firearmDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.Firearm, error) { firearm := &models.Firearm{} err := c.db.Collection(firearmName).FindOne(ctx, filter).Decode(&firearm) if err != nil { @@ -36,9 +37,9 @@ func (c *firearmDatabase) FindOne(ctx context.Context, filter interface{}) (*mod return firearm, nil } -func (c *firearmDatabase) Find(ctx context.Context, filter interface{}) ([]models.Firearm, error) { +func (c *firearmDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.Firearm, error) { var firearms []models.Firearm - err := c.db.Collection(firearmName).Find(ctx, filter).Decode(&firearms) + err := c.db.Collection(firearmName).Find(ctx, filter, opts...).Decode(&firearms) if err != nil { return nil, err } diff --git a/databases/mocks/CollectionHelper.go b/databases/mocks/CollectionHelper.go index 1c1d39e..1685ab6 100644 --- a/databases/mocks/CollectionHelper.go +++ b/databases/mocks/CollectionHelper.go @@ -7,6 +7,8 @@ import ( databases "github.com/linesmerrill/police-cad-api/databases" mock "github.com/stretchr/testify/mock" + + options "go.mongodb.org/mongo-driver/mongo/options" ) // CollectionHelper is an autogenerated mock type for the CollectionHelper type @@ -14,13 +16,20 @@ type CollectionHelper struct { mock.Mock } -// Find provides a mock function with given fields: _a0, _a1 -func (_m *CollectionHelper) Find(_a0 context.Context, _a1 interface{}) databases.CursorHelper { - ret := _m.Called(_a0, _a1) +// Find provides a mock function with given fields: _a0, _a1, _a2 +func (_m *CollectionHelper) Find(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOptions) databases.CursorHelper { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 databases.CursorHelper - if rf, ok := ret.Get(0).(func(context.Context, interface{}) databases.CursorHelper); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOptions) databases.CursorHelper); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(databases.CursorHelper) @@ -30,13 +39,20 @@ func (_m *CollectionHelper) Find(_a0 context.Context, _a1 interface{}) databases return r0 } -// FindOne provides a mock function with given fields: _a0, _a1 -func (_m *CollectionHelper) FindOne(_a0 context.Context, _a1 interface{}) databases.SingleResultHelper { - ret := _m.Called(_a0, _a1) +// FindOne provides a mock function with given fields: _a0, _a1, _a2 +func (_m *CollectionHelper) FindOne(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOneOptions) databases.SingleResultHelper { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 databases.SingleResultHelper - if rf, ok := ret.Get(0).(func(context.Context, interface{}) databases.SingleResultHelper); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOneOptions) databases.SingleResultHelper); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(databases.SingleResultHelper) diff --git a/databases/mocks/FirearmDatabase.go b/databases/mocks/FirearmDatabase.go index 86231aa..9c0536e 100644 --- a/databases/mocks/FirearmDatabase.go +++ b/databases/mocks/FirearmDatabase.go @@ -8,6 +8,8 @@ import ( mock "github.com/stretchr/testify/mock" models "github.com/linesmerrill/police-cad-api/models" + + options "go.mongodb.org/mongo-driver/mongo/options" ) // FirearmDatabase is an autogenerated mock type for the FirearmDatabase type @@ -15,13 +17,20 @@ type FirearmDatabase struct { mock.Mock } -// Find provides a mock function with given fields: ctx, filter -func (_m *FirearmDatabase) Find(ctx context.Context, filter interface{}) ([]models.Firearm, error) { - ret := _m.Called(ctx, filter) +// Find provides a mock function with given fields: ctx, filter, opts +func (_m *FirearmDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.Firearm, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, filter) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 []models.Firearm - if rf, ok := ret.Get(0).(func(context.Context, interface{}) []models.Firearm); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOptions) []models.Firearm); ok { + r0 = rf(ctx, filter, opts...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]models.Firearm) @@ -29,8 +38,8 @@ func (_m *FirearmDatabase) Find(ctx context.Context, filter interface{}) ([]mode } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOptions) error); ok { + r1 = rf(ctx, filter, opts...) } else { r1 = ret.Error(1) } @@ -38,13 +47,20 @@ func (_m *FirearmDatabase) Find(ctx context.Context, filter interface{}) ([]mode return r0, r1 } -// FindOne provides a mock function with given fields: ctx, filter -func (_m *FirearmDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Firearm, error) { - ret := _m.Called(ctx, filter) +// FindOne provides a mock function with given fields: ctx, filter, opts +func (_m *FirearmDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.Firearm, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, filter) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 *models.Firearm - if rf, ok := ret.Get(0).(func(context.Context, interface{}) *models.Firearm); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOneOptions) *models.Firearm); ok { + r0 = rf(ctx, filter, opts...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*models.Firearm) @@ -52,8 +68,8 @@ func (_m *FirearmDatabase) FindOne(ctx context.Context, filter interface{}) (*mo } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOneOptions) error); ok { + r1 = rf(ctx, filter, opts...) } else { r1 = ret.Error(1) } diff --git a/databases/utility.go b/databases/utility.go new file mode 100644 index 0000000..a72095f --- /dev/null +++ b/databases/utility.go @@ -0,0 +1,23 @@ +package databases + +import "go.mongodb.org/mongo-driver/mongo/options" + +type mongoPaginate struct { + limit int64 + page int64 +} + +func newMongoPaginate(limit, page int) *mongoPaginate { + return &mongoPaginate{ + limit: int64(limit), + page: int64(page), + } +} + +func (mp *mongoPaginate) getPaginatedOpts() *options.FindOptions { + l := mp.limit + skip := mp.page*mp.limit - mp.limit + fOpt := options.FindOptions{Limit: &l, Skip: &skip} + + return &fOpt +} diff --git a/databases/vehicle.go b/databases/vehicle.go index 6021322..0532dd9 100644 --- a/databases/vehicle.go +++ b/databases/vehicle.go @@ -1,19 +1,20 @@ package databases -//go generate: mockery --name VehicleDatabase +// go generate: mockery --name VehicleDatabase import ( "context" "github.com/linesmerrill/police-cad-api/models" + "go.mongodb.org/mongo-driver/mongo/options" ) const vehicleName = "vehicles" // VehicleDatabase contains the methods to use with the vehicle database type VehicleDatabase interface { - FindOne(ctx context.Context, filter interface{}) (*models.Vehicle, error) - Find(ctx context.Context, filter interface{}) ([]models.Vehicle, error) + FindOne(context.Context, interface{}, ...*options.FindOneOptions) (*models.Vehicle, error) + Find(context.Context, interface{}, ...*options.FindOptions) ([]models.Vehicle, error) } type vehicleDatabase struct { @@ -27,18 +28,18 @@ func NewVehicleDatabase(db DatabaseHelper) VehicleDatabase { } } -func (c *vehicleDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Vehicle, error) { +func (c *vehicleDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.Vehicle, error) { vehicle := &models.Vehicle{} - err := c.db.Collection(vehicleName).FindOne(ctx, filter).Decode(&vehicle) + err := c.db.Collection(vehicleName).FindOne(ctx, filter, opts...).Decode(&vehicle) if err != nil { return nil, err } return vehicle, nil } -func (c *vehicleDatabase) Find(ctx context.Context, filter interface{}) ([]models.Vehicle, error) { +func (c *vehicleDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.Vehicle, error) { var vehicles []models.Vehicle - err := c.db.Collection(vehicleName).Find(ctx, filter).Decode(&vehicles) + err := c.db.Collection(vehicleName).Find(ctx, filter, opts...).Decode(&vehicles) if err != nil { return nil, err } From 1e9be5d46723608174741b390efbc5d917b17399 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Sun, 2 Apr 2023 14:40:57 -0700 Subject: [PATCH 02/15] update tests --- api/handlers/api.go | 2 +- api/handlers/call_test.go | 22 +++++++------- api/handlers/civilian_test.go | 22 +++++++------- api/handlers/community_test.go | 8 ++--- api/handlers/emsVehicle_test.go | 22 +++++++------- api/handlers/ems_test.go | 22 +++++++------- api/handlers/firearm_test.go | 22 +++++++------- api/handlers/search/name_test.go | 8 ++--- api/handlers/user_test.go | 10 +++--- api/handlers/vehicle_test.go | 22 +++++++------- databases/mocks/CallDatabase.go | 44 ++++++++++++++++++--------- databases/mocks/CivilianDatabase.go | 44 ++++++++++++++++++--------- databases/mocks/EmsDatabase.go | 44 ++++++++++++++++++--------- databases/mocks/EmsVehicleDatabase.go | 44 ++++++++++++++++++--------- databases/mocks/VehicleDatabase.go | 44 ++++++++++++++++++--------- 15 files changed, 230 insertions(+), 150 deletions(-) diff --git a/api/handlers/api.go b/api/handlers/api.go index 66939e6..25e3446 100644 --- a/api/handlers/api.go +++ b/api/handlers/api.go @@ -57,7 +57,7 @@ func (a *App) New() *mux.Router { apiCreate.Handle("/vehicles", api.Middleware(http.HandlerFunc(v.VehicleHandler))).Methods("GET") apiCreate.Handle("/vehicles/user/{user_id}", api.Middleware(http.HandlerFunc(v.VehiclesByUserIDHandler))).Methods("GET") apiCreate.Handle("/firearm/{firearm_id}", api.Middleware(http.HandlerFunc(f.FirearmByIDHandler))).Methods("GET") - apiCreate.Handle("/firearms", api.Middleware(api.Pagination(http.HandlerFunc(f.FirearmHandler)))).Methods("GET") + apiCreate.Handle("/firearms", api.Middleware(http.HandlerFunc(f.FirearmHandler))).Methods("GET") apiCreate.Handle("/firearms/user/{user_id}", api.Middleware(http.HandlerFunc(f.FirearmsByUserIDHandler))).Methods("GET") apiCreate.Handle("/ems/{ems_id}", api.Middleware(http.HandlerFunc(e.EmsByIDHandler))).Methods("GET") apiCreate.Handle("/ems", api.Middleware(http.HandlerFunc(e.EmsHandler))).Methods("GET") diff --git a/api/handlers/call_test.go b/api/handlers/call_test.go index 7b74819..48acac4 100644 --- a/api/handlers/call_test.go +++ b/api/handlers/call_test.go @@ -239,7 +239,7 @@ func TestCall_CallHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Call) *arg = []models.Call{{Details: models.CallDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) @@ -284,7 +284,7 @@ func TestCall_CallHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) @@ -333,7 +333,7 @@ func TestCall_CallHandlerSuccess(t *testing.T) { *arg = []models.Call{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) @@ -380,7 +380,7 @@ func TestCall_CallHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Call) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) @@ -433,7 +433,7 @@ func TestCall_CallsByCommunityIDHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Call) *arg = []models.Call{{Details: models.CallDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) @@ -480,7 +480,7 @@ func TestCall_CallsByCommunityIDHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) @@ -527,7 +527,7 @@ func TestCall_CallsByCommunityIDHandlerActiveCommunityIDFailedToFindOne(t *testi client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) @@ -579,7 +579,7 @@ func TestCall_CallsByCommunityIDHandlerSuccess(t *testing.T) { *arg = []models.Call{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) @@ -630,7 +630,7 @@ func TestCall_CallsByCommunityIDHandlerSuccessWithActiveCommunityID(t *testing.T *arg = []models.Call{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) @@ -681,7 +681,7 @@ func TestCall_CallsByCommunityIDHandlerSuccessWithNullCommunityID(t *testing.T) *arg = []models.Call{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) @@ -731,7 +731,7 @@ func TestCall_CallsByCommunityIDHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Call) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "calls").Return(conn) callDatabase := databases.NewCallDatabase(db) diff --git a/api/handlers/civilian_test.go b/api/handlers/civilian_test.go index 228b393..3c8ce44 100644 --- a/api/handlers/civilian_test.go +++ b/api/handlers/civilian_test.go @@ -239,7 +239,7 @@ func TestCivilian_CivilianHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Civilian) *arg = []models.Civilian{{Details: models.CivilianDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -284,7 +284,7 @@ func TestCivilian_CivilianHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -333,7 +333,7 @@ func TestCivilian_CivilianHandlerSuccess(t *testing.T) { *arg = []models.Civilian{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -380,7 +380,7 @@ func TestCivilian_CivilianHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Civilian) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -430,7 +430,7 @@ func TestCivilian_CiviliansByUserIDHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Civilian) *arg = []models.Civilian{{Details: models.CivilianDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -475,7 +475,7 @@ func TestCivilian_CiviliansByUserIDHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -520,7 +520,7 @@ func TestCivilian_CiviliansByUserIDHandlerActiveCommunityIDFailedToFindOne(t *te client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -569,7 +569,7 @@ func TestCivilian_CiviliansByUserIDHandlerSuccess(t *testing.T) { *arg = []models.Civilian{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -617,7 +617,7 @@ func TestCivilian_CiviliansByUserIDHandlerSuccessWithActiveCommunityID(t *testin *arg = []models.Civilian{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -665,7 +665,7 @@ func TestCivilian_CiviliansByUserIDHandlerSuccessWithNullCommunityID(t *testing. *arg = []models.Civilian{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -712,7 +712,7 @@ func TestCivilian_CiviliansByUserIDHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Civilian) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) diff --git a/api/handlers/community_test.go b/api/handlers/community_test.go index b2d0e24..41273ed 100644 --- a/api/handlers/community_test.go +++ b/api/handlers/community_test.go @@ -436,7 +436,7 @@ func TestCommunity_CommunitiesByOwnerIDHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Community) *arg = []models.Community{{Details: models.CommunityDetails{ActivePanics: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "communities").Return(conn) communityDatabase := databases.NewCommunityDatabase(db) @@ -482,7 +482,7 @@ func TestCommunity_CommunitiesByOwnerIDHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "communities").Return(conn) communityDatabase := databases.NewCommunityDatabase(db) @@ -532,7 +532,7 @@ func TestCommunity_CommunitiesByOwnerIDHandlerSuccess(t *testing.T) { *arg = []models.Community{{ID: "608cafe595eb9dc05379b7f4"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "communities").Return(conn) communityDatabase := databases.NewCommunityDatabase(db) @@ -580,7 +580,7 @@ func TestUser_CommunitiesByOwnerIDHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Community) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "communities").Return(conn) communityDatabase := databases.NewCommunityDatabase(db) diff --git a/api/handlers/emsVehicle_test.go b/api/handlers/emsVehicle_test.go index 7621b2d..a32c513 100644 --- a/api/handlers/emsVehicle_test.go +++ b/api/handlers/emsVehicle_test.go @@ -239,7 +239,7 @@ func TestEmsVehicle_EmsVehicleHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.EmsVehicle) *arg = []models.EmsVehicle{{Details: models.EmsVehicleDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) @@ -284,7 +284,7 @@ func TestEmsVehicle_EmsVehicleHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) @@ -333,7 +333,7 @@ func TestEmsVehicle_EmsVehicleHandlerSuccess(t *testing.T) { *arg = []models.EmsVehicle{{ID: "5fc51f58c72ff10004dca382"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) @@ -380,7 +380,7 @@ func TestEmsVehicle_EmsVehicleHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.EmsVehicle) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) @@ -430,7 +430,7 @@ func TestEmsVehicle_EmsVehiclesByUserIDHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.EmsVehicle) *arg = []models.EmsVehicle{{Details: models.EmsVehicleDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) @@ -475,7 +475,7 @@ func TestEmsVehicle_EmsVehiclesByUserIDHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) @@ -520,7 +520,7 @@ func TestEmsVehicle_EmsVehiclesByUserIDHandlerActiveCommunityIDFailedToFindOne(t client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) @@ -569,7 +569,7 @@ func TestEmsVehicle_EmsVehiclesByUserIDHandlerSuccess(t *testing.T) { *arg = []models.EmsVehicle{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) @@ -617,7 +617,7 @@ func TestEmsVehicle_EmsVehiclesByUserIDHandlerSuccessWithActiveCommunityID(t *te *arg = []models.EmsVehicle{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) @@ -665,7 +665,7 @@ func TestEmsVehicle_EmsVehiclesByUserIDHandlerSuccessWithNullCommunityID(t *test *arg = []models.EmsVehicle{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) @@ -712,7 +712,7 @@ func TestEmsVehicle_EmsVehiclesByUserIDHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.EmsVehicle) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "emsvehicles").Return(conn) emsVehicleDatabase := databases.NewEmsVehicleDatabase(db) diff --git a/api/handlers/ems_test.go b/api/handlers/ems_test.go index 4f2f0d3..07a6f29 100644 --- a/api/handlers/ems_test.go +++ b/api/handlers/ems_test.go @@ -239,7 +239,7 @@ func TestEms_EmsHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Ems) *arg = []models.Ems{{Details: models.EmsDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) @@ -284,7 +284,7 @@ func TestEms_EmsHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) @@ -333,7 +333,7 @@ func TestEms_EmsHandlerSuccess(t *testing.T) { *arg = []models.Ems{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) @@ -380,7 +380,7 @@ func TestEms_EmsHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Ems) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) @@ -430,7 +430,7 @@ func TestEms_EmsByUserIDHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Ems) *arg = []models.Ems{{Details: models.EmsDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) @@ -475,7 +475,7 @@ func TestEms_EmsByUserIDHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) @@ -520,7 +520,7 @@ func TestEms_EmsByUserIDHandlerActiveCommunityIDFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) @@ -569,7 +569,7 @@ func TestEms_EmsByUserIDHandlerSuccess(t *testing.T) { *arg = []models.Ems{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) @@ -617,7 +617,7 @@ func TestEms_EmsByUserIDHandlerSuccessWithActiveCommunityID(t *testing.T) { *arg = []models.Ems{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) @@ -665,7 +665,7 @@ func TestEms_EmsByUserIDHandlerSuccessWithNullCommunityID(t *testing.T) { *arg = []models.Ems{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) @@ -712,7 +712,7 @@ func TestEms_EmsByUserIDHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Ems) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "ems").Return(conn) emsDatabase := databases.NewEmsDatabase(db) diff --git a/api/handlers/firearm_test.go b/api/handlers/firearm_test.go index beac672..6431ca4 100644 --- a/api/handlers/firearm_test.go +++ b/api/handlers/firearm_test.go @@ -239,7 +239,7 @@ func TestFirearm_FirearmHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Firearm) *arg = []models.Firearm{{Details: models.FirearmDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) @@ -284,7 +284,7 @@ func TestFirearm_FirearmHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) @@ -333,7 +333,7 @@ func TestFirearm_FirearmHandlerSuccess(t *testing.T) { *arg = []models.Firearm{{ID: "5fc51f58c72ff10004dca382"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) @@ -380,7 +380,7 @@ func TestFirearm_FirearmHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Firearm) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) @@ -430,7 +430,7 @@ func TestFirearm_FirearmsByUserIDHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Firearm) *arg = []models.Firearm{{Details: models.FirearmDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) @@ -475,7 +475,7 @@ func TestFirearm_FirearmsByUserIDHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) @@ -520,7 +520,7 @@ func TestFirearm_FirearmsByUserIDHandlerActiveCommunityIDFailedToFindOne(t *test client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) @@ -569,7 +569,7 @@ func TestFirearm_FirearmsByUserIDHandlerSuccess(t *testing.T) { *arg = []models.Firearm{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) @@ -617,7 +617,7 @@ func TestFirearm_FirearmsByUserIDHandlerSuccessWithActiveCommunityID(t *testing. *arg = []models.Firearm{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) @@ -665,7 +665,7 @@ func TestFirearm_FirearmsByUserIDHandlerSuccessWithNullCommunityID(t *testing.T) *arg = []models.Firearm{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) @@ -712,7 +712,7 @@ func TestFirearm_FirearmsByUserIDHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Firearm) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) firearmDatabase := databases.NewFirearmDatabase(db) diff --git a/api/handlers/search/name_test.go b/api/handlers/search/name_test.go index b9617f6..066db80 100644 --- a/api/handlers/search/name_test.go +++ b/api/handlers/search/name_test.go @@ -79,7 +79,7 @@ func TestName_NameSearchHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Civilian) *arg = []models.Civilian{{Details: models.CivilianDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -124,7 +124,7 @@ func TestName_NameSearchHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -173,7 +173,7 @@ func TestName_NameSearchHandlerSuccess(t *testing.T) { *arg = []models.Civilian{{ID: "5fc51f58c72ff10004dca382"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) @@ -220,7 +220,7 @@ func TestName_NameSearchHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Civilian) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) civilianDatabase := databases.NewCivilianDatabase(db) diff --git a/api/handlers/user_test.go b/api/handlers/user_test.go index 9f55325..6b86097 100644 --- a/api/handlers/user_test.go +++ b/api/handlers/user_test.go @@ -266,7 +266,7 @@ func TestUser_UsersFindAllHandlerInvalidID(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mocked-error")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "users").Return(conn) userDatabase := databases.NewUserDatabase(db) @@ -319,7 +319,7 @@ func TestUser_UsersFindAllHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.User) (*arg) = []models.User{{Details: models.UserDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "users").Return(conn) userDatabase := databases.NewUserDatabase(db) @@ -365,7 +365,7 @@ func TestUser_UsersFindAllHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "users").Return(conn) userDatabase := databases.NewUserDatabase(db) @@ -414,7 +414,7 @@ func TestUser_UsersFindAllHandlerSuccess(t *testing.T) { arg := args.Get(0).(*[]models.User) (*arg) = []models.User{{ID: "608cafd695eb9dc05379b7f3"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "users").Return(conn) userDatabase := databases.NewUserDatabase(db) @@ -462,7 +462,7 @@ func TestUser_UsersFindAllHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.User) (*arg) = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "users").Return(conn) userDatabase := databases.NewUserDatabase(db) diff --git a/api/handlers/vehicle_test.go b/api/handlers/vehicle_test.go index 35b492e..bfff1ed 100644 --- a/api/handlers/vehicle_test.go +++ b/api/handlers/vehicle_test.go @@ -239,7 +239,7 @@ func TestVehicle_VehicleHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Vehicle) *arg = []models.Vehicle{{Details: models.VehicleDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) @@ -284,7 +284,7 @@ func TestVehicle_VehicleHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) @@ -333,7 +333,7 @@ func TestVehicle_VehicleHandlerSuccess(t *testing.T) { *arg = []models.Vehicle{{ID: "5fc51f58c72ff10004dca382"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) @@ -380,7 +380,7 @@ func TestVehicle_VehicleHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Vehicle) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) @@ -430,7 +430,7 @@ func TestVehicle_VehiclesByUserIDHandlerJsonMarshalError(t *testing.T) { arg := args.Get(0).(*[]models.Vehicle) *arg = []models.Vehicle{{Details: models.VehicleDetails{CreatedAt: x}}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) @@ -475,7 +475,7 @@ func TestVehicle_VehiclesByUserIDHandlerFailedToFindOne(t *testing.T) { client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) @@ -520,7 +520,7 @@ func TestVehicle_VehiclesByUserIDHandlerActiveCommunityIDFailedToFindOne(t *test client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) db.(*MockDatabaseHelper).On("Client").Return(client) singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) @@ -569,7 +569,7 @@ func TestVehicle_VehiclesByUserIDHandlerSuccess(t *testing.T) { *arg = []models.Vehicle{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) @@ -617,7 +617,7 @@ func TestVehicle_VehiclesByUserIDHandlerSuccessWithActiveCommunityID(t *testing. *arg = []models.Vehicle{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) @@ -665,7 +665,7 @@ func TestVehicle_VehiclesByUserIDHandlerSuccessWithNullCommunityID(t *testing.T) *arg = []models.Vehicle{{ID: "5fc51f36c72ff10004dca381"}} }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(singleResultHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) @@ -712,7 +712,7 @@ func TestVehicle_VehiclesByUserIDHandlerEmptyResponse(t *testing.T) { arg := args.Get(0).(*[]models.Vehicle) *arg = nil }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything).Return(cursorHelper) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) vehicleDatabase := databases.NewVehicleDatabase(db) diff --git a/databases/mocks/CallDatabase.go b/databases/mocks/CallDatabase.go index ee26e1a..14b1bcc 100644 --- a/databases/mocks/CallDatabase.go +++ b/databases/mocks/CallDatabase.go @@ -8,6 +8,8 @@ import ( mock "github.com/stretchr/testify/mock" models "github.com/linesmerrill/police-cad-api/models" + + options "go.mongodb.org/mongo-driver/mongo/options" ) // CallDatabase is an autogenerated mock type for the CallDatabase type @@ -15,13 +17,20 @@ type CallDatabase struct { mock.Mock } -// Find provides a mock function with given fields: ctx, filter -func (_m *CallDatabase) Find(ctx context.Context, filter interface{}) ([]models.Call, error) { - ret := _m.Called(ctx, filter) +// Find provides a mock function with given fields: _a0, _a1, _a2 +func (_m *CallDatabase) Find(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOptions) ([]models.Call, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 []models.Call - if rf, ok := ret.Get(0).(func(context.Context, interface{}) []models.Call); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOptions) []models.Call); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]models.Call) @@ -29,8 +38,8 @@ func (_m *CallDatabase) Find(ctx context.Context, filter interface{}) ([]models. } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOptions) error); ok { + r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } @@ -38,13 +47,20 @@ func (_m *CallDatabase) Find(ctx context.Context, filter interface{}) ([]models. return r0, r1 } -// FindOne provides a mock function with given fields: ctx, filter -func (_m *CallDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Call, error) { - ret := _m.Called(ctx, filter) +// FindOne provides a mock function with given fields: _a0, _a1, _a2 +func (_m *CallDatabase) FindOne(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOneOptions) (*models.Call, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 *models.Call - if rf, ok := ret.Get(0).(func(context.Context, interface{}) *models.Call); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOneOptions) *models.Call); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*models.Call) @@ -52,8 +68,8 @@ func (_m *CallDatabase) FindOne(ctx context.Context, filter interface{}) (*model } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOneOptions) error); ok { + r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } diff --git a/databases/mocks/CivilianDatabase.go b/databases/mocks/CivilianDatabase.go index aebd44f..20e67fb 100644 --- a/databases/mocks/CivilianDatabase.go +++ b/databases/mocks/CivilianDatabase.go @@ -8,6 +8,8 @@ import ( mock "github.com/stretchr/testify/mock" models "github.com/linesmerrill/police-cad-api/models" + + options "go.mongodb.org/mongo-driver/mongo/options" ) // CivilianDatabase is an autogenerated mock type for the CivilianDatabase type @@ -15,13 +17,20 @@ type CivilianDatabase struct { mock.Mock } -// Find provides a mock function with given fields: ctx, filter -func (_m *CivilianDatabase) Find(ctx context.Context, filter interface{}) ([]models.Civilian, error) { - ret := _m.Called(ctx, filter) +// Find provides a mock function with given fields: _a0, _a1, _a2 +func (_m *CivilianDatabase) Find(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOptions) ([]models.Civilian, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 []models.Civilian - if rf, ok := ret.Get(0).(func(context.Context, interface{}) []models.Civilian); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOptions) []models.Civilian); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]models.Civilian) @@ -29,8 +38,8 @@ func (_m *CivilianDatabase) Find(ctx context.Context, filter interface{}) ([]mod } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOptions) error); ok { + r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } @@ -38,13 +47,20 @@ func (_m *CivilianDatabase) Find(ctx context.Context, filter interface{}) ([]mod return r0, r1 } -// FindOne provides a mock function with given fields: ctx, filter -func (_m *CivilianDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Civilian, error) { - ret := _m.Called(ctx, filter) +// FindOne provides a mock function with given fields: _a0, _a1, _a2 +func (_m *CivilianDatabase) FindOne(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOneOptions) (*models.Civilian, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 *models.Civilian - if rf, ok := ret.Get(0).(func(context.Context, interface{}) *models.Civilian); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOneOptions) *models.Civilian); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*models.Civilian) @@ -52,8 +68,8 @@ func (_m *CivilianDatabase) FindOne(ctx context.Context, filter interface{}) (*m } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOneOptions) error); ok { + r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } diff --git a/databases/mocks/EmsDatabase.go b/databases/mocks/EmsDatabase.go index 221b045..dbad106 100644 --- a/databases/mocks/EmsDatabase.go +++ b/databases/mocks/EmsDatabase.go @@ -8,6 +8,8 @@ import ( mock "github.com/stretchr/testify/mock" models "github.com/linesmerrill/police-cad-api/models" + + options "go.mongodb.org/mongo-driver/mongo/options" ) // EmsDatabase is an autogenerated mock type for the EmsDatabase type @@ -15,13 +17,20 @@ type EmsDatabase struct { mock.Mock } -// Find provides a mock function with given fields: ctx, filter -func (_m *EmsDatabase) Find(ctx context.Context, filter interface{}) ([]models.Ems, error) { - ret := _m.Called(ctx, filter) +// Find provides a mock function with given fields: _a0, _a1, _a2 +func (_m *EmsDatabase) Find(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOptions) ([]models.Ems, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 []models.Ems - if rf, ok := ret.Get(0).(func(context.Context, interface{}) []models.Ems); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOptions) []models.Ems); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]models.Ems) @@ -29,8 +38,8 @@ func (_m *EmsDatabase) Find(ctx context.Context, filter interface{}) ([]models.E } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOptions) error); ok { + r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } @@ -38,13 +47,20 @@ func (_m *EmsDatabase) Find(ctx context.Context, filter interface{}) ([]models.E return r0, r1 } -// FindOne provides a mock function with given fields: ctx, filter -func (_m *EmsDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Ems, error) { - ret := _m.Called(ctx, filter) +// FindOne provides a mock function with given fields: _a0, _a1, _a2 +func (_m *EmsDatabase) FindOne(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOneOptions) (*models.Ems, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 *models.Ems - if rf, ok := ret.Get(0).(func(context.Context, interface{}) *models.Ems); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOneOptions) *models.Ems); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*models.Ems) @@ -52,8 +68,8 @@ func (_m *EmsDatabase) FindOne(ctx context.Context, filter interface{}) (*models } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOneOptions) error); ok { + r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } diff --git a/databases/mocks/EmsVehicleDatabase.go b/databases/mocks/EmsVehicleDatabase.go index 2ea79db..1af2c8e 100644 --- a/databases/mocks/EmsVehicleDatabase.go +++ b/databases/mocks/EmsVehicleDatabase.go @@ -8,6 +8,8 @@ import ( mock "github.com/stretchr/testify/mock" models "github.com/linesmerrill/police-cad-api/models" + + options "go.mongodb.org/mongo-driver/mongo/options" ) // EmsVehicleDatabase is an autogenerated mock type for the EmsVehicleDatabase type @@ -15,13 +17,20 @@ type EmsVehicleDatabase struct { mock.Mock } -// Find provides a mock function with given fields: ctx, filter -func (_m *EmsVehicleDatabase) Find(ctx context.Context, filter interface{}) ([]models.EmsVehicle, error) { - ret := _m.Called(ctx, filter) +// Find provides a mock function with given fields: _a0, _a1, _a2 +func (_m *EmsVehicleDatabase) Find(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOptions) ([]models.EmsVehicle, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 []models.EmsVehicle - if rf, ok := ret.Get(0).(func(context.Context, interface{}) []models.EmsVehicle); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOptions) []models.EmsVehicle); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]models.EmsVehicle) @@ -29,8 +38,8 @@ func (_m *EmsVehicleDatabase) Find(ctx context.Context, filter interface{}) ([]m } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOptions) error); ok { + r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } @@ -38,13 +47,20 @@ func (_m *EmsVehicleDatabase) Find(ctx context.Context, filter interface{}) ([]m return r0, r1 } -// FindOne provides a mock function with given fields: ctx, filter -func (_m *EmsVehicleDatabase) FindOne(ctx context.Context, filter interface{}) (*models.EmsVehicle, error) { - ret := _m.Called(ctx, filter) +// FindOne provides a mock function with given fields: _a0, _a1, _a2 +func (_m *EmsVehicleDatabase) FindOne(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOneOptions) (*models.EmsVehicle, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 *models.EmsVehicle - if rf, ok := ret.Get(0).(func(context.Context, interface{}) *models.EmsVehicle); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOneOptions) *models.EmsVehicle); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*models.EmsVehicle) @@ -52,8 +68,8 @@ func (_m *EmsVehicleDatabase) FindOne(ctx context.Context, filter interface{}) ( } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOneOptions) error); ok { + r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } diff --git a/databases/mocks/VehicleDatabase.go b/databases/mocks/VehicleDatabase.go index 7514f8f..2a30372 100644 --- a/databases/mocks/VehicleDatabase.go +++ b/databases/mocks/VehicleDatabase.go @@ -8,6 +8,8 @@ import ( mock "github.com/stretchr/testify/mock" models "github.com/linesmerrill/police-cad-api/models" + + options "go.mongodb.org/mongo-driver/mongo/options" ) // VehicleDatabase is an autogenerated mock type for the VehicleDatabase type @@ -15,13 +17,20 @@ type VehicleDatabase struct { mock.Mock } -// Find provides a mock function with given fields: ctx, filter -func (_m *VehicleDatabase) Find(ctx context.Context, filter interface{}) ([]models.Vehicle, error) { - ret := _m.Called(ctx, filter) +// Find provides a mock function with given fields: _a0, _a1, _a2 +func (_m *VehicleDatabase) Find(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOptions) ([]models.Vehicle, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 []models.Vehicle - if rf, ok := ret.Get(0).(func(context.Context, interface{}) []models.Vehicle); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOptions) []models.Vehicle); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]models.Vehicle) @@ -29,8 +38,8 @@ func (_m *VehicleDatabase) Find(ctx context.Context, filter interface{}) ([]mode } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOptions) error); ok { + r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } @@ -38,13 +47,20 @@ func (_m *VehicleDatabase) Find(ctx context.Context, filter interface{}) ([]mode return r0, r1 } -// FindOne provides a mock function with given fields: ctx, filter -func (_m *VehicleDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Vehicle, error) { - ret := _m.Called(ctx, filter) +// FindOne provides a mock function with given fields: _a0, _a1, _a2 +func (_m *VehicleDatabase) FindOne(_a0 context.Context, _a1 interface{}, _a2 ...*options.FindOneOptions) (*models.Vehicle, error) { + _va := make([]interface{}, len(_a2)) + for _i := range _a2 { + _va[_i] = _a2[_i] + } + var _ca []interface{} + _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) var r0 *models.Vehicle - if rf, ok := ret.Get(0).(func(context.Context, interface{}) *models.Vehicle); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOneOptions) *models.Vehicle); ok { + r0 = rf(_a0, _a1, _a2...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*models.Vehicle) @@ -52,8 +68,8 @@ func (_m *VehicleDatabase) FindOne(ctx context.Context, filter interface{}) (*mo } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOneOptions) error); ok { + r1 = rf(_a0, _a1, _a2...) } else { r1 = ret.Error(1) } From a8427f683087444bc2953549889ac43b431fe352 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Sun, 2 Apr 2023 14:42:42 -0700 Subject: [PATCH 03/15] remove unused file --- databases/utility.go | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 databases/utility.go diff --git a/databases/utility.go b/databases/utility.go deleted file mode 100644 index a72095f..0000000 --- a/databases/utility.go +++ /dev/null @@ -1,23 +0,0 @@ -package databases - -import "go.mongodb.org/mongo-driver/mongo/options" - -type mongoPaginate struct { - limit int64 - page int64 -} - -func newMongoPaginate(limit, page int) *mongoPaginate { - return &mongoPaginate{ - limit: int64(limit), - page: int64(page), - } -} - -func (mp *mongoPaginate) getPaginatedOpts() *options.FindOptions { - l := mp.limit - skip := mp.page*mp.limit - mp.limit - fOpt := options.FindOptions{Limit: &l, Skip: &skip} - - return &fOpt -} From 6ff6a98b178b6fdcd4243cd2f8336a786f3c1e6f Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Tue, 23 May 2023 16:52:08 -0700 Subject: [PATCH 04/15] add paginated calls for civilian listing --- api/handlers/civilian.go | 36 ++++++++++--- api/handlers/civilian_test.go | 96 +++++++++++++++++++++++++++++++++++ config/config_test.go | 7 +++ 3 files changed, 132 insertions(+), 7 deletions(-) diff --git a/api/handlers/civilian.go b/api/handlers/civilian.go index 7577c8a..2b1c226 100644 --- a/api/handlers/civilian.go +++ b/api/handlers/civilian.go @@ -18,6 +18,11 @@ import ( "github.com/linesmerrill/police-cad-api/models" ) +var ( + // Page denotes the starting Page for pagination results + Page = 1 +) + // Civilian exported for testing purposes type Civilian struct { DB databases.CivilianDatabase @@ -30,10 +35,7 @@ func (c Civilian) CivilianHandler(w http.ResponseWriter, r *http.Request) { zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) } limit64 := int64(Limit) - Page, err := strconv.Atoi(r.URL.Query().Get("page")) - if err != nil { - zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) - } + Page = getPage(Page, r) skip64 := int64(Page) dbResp, err := c.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { @@ -85,6 +87,13 @@ func (c Civilian) CivilianByIDHandler(w http.ResponseWriter, r *http.Request) { func (c Civilian) CiviliansByUserIDHandler(w http.ResponseWriter, r *http.Request) { userID := mux.Vars(r)["user_id"] activeCommunityID := r.URL.Query().Get("active_community_id") + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page) zap.S().Debugf("user_id: '%v'", userID) zap.S().Debugf("active_community: '%v'", activeCommunityID) @@ -97,12 +106,12 @@ func (c Civilian) CiviliansByUserIDHandler(w http.ResponseWriter, r *http.Reques // // Likewise, if the user is not in a community, then we will display only the civilians // that are not in a community - var err error + err = nil if activeCommunityID != "" && activeCommunityID != "null" && activeCommunityID != "undefined" { dbResp, err = c.DB.Find(context.TODO(), bson.M{ "civilian.userID": userID, "civilian.activeCommunityID": activeCommunityID, - }) + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get civilians with active community id", http.StatusNotFound, w, err) return @@ -114,7 +123,7 @@ func (c Civilian) CiviliansByUserIDHandler(w http.ResponseWriter, r *http.Reques {"civilian.activeCommunityID": nil}, {"civilian.activeCommunityID": ""}, }, - }) + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get civilians with empty active community id", http.StatusNotFound, w, err) return @@ -134,3 +143,16 @@ func (c Civilian) CiviliansByUserIDHandler(w http.ResponseWriter, r *http.Reques w.WriteHeader(http.StatusOK) w.Write(b) } + +func getPage(Page int, r *http.Request) int { + if r.URL.Query().Get("page") == "" { + zap.S().Warnf("page not set, using default of %v", Page) + } else { + var err error + Page, err = strconv.Atoi(r.URL.Query().Get("page")) + if err != nil { + zap.S().Errorf(fmt.Sprintf("error parsing page number: %v", err)) + } + } + return Page +} diff --git a/api/handlers/civilian_test.go b/api/handlers/civilian_test.go index 3c8ce44..c92c341 100644 --- a/api/handlers/civilian_test.go +++ b/api/handlers/civilian_test.go @@ -356,6 +356,102 @@ func TestCivilian_CivilianHandlerSuccess(t *testing.T) { assert.Equal(t, "5fc51f36c72ff10004dca381", testCivilian[0].ID) } +func TestCivilian_CivilianHandlerPaginationSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians?limit=5&page=1", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Civilian) + *arg = []models.Civilian{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CivilianHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testCivilian []models.Civilian + _ = json.Unmarshal(rr.Body.Bytes(), &testCivilian) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testCivilian[0].ID) +} + +func TestCivilian_CivilianHandlerPaginationInvalidPageEntry(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians?limit=5&page='1'", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Civilian) + *arg = []models.Civilian{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CivilianHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testCivilian []models.Civilian + _ = json.Unmarshal(rr.Body.Bytes(), &testCivilian) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testCivilian[0].ID) +} + func TestCivilian_CivilianHandlerEmptyResponse(t *testing.T) { req, err := http.NewRequest("GET", "/api/v1/civilian", nil) if err != nil { diff --git a/config/config_test.go b/config/config_test.go index 7057e6a..bc6fa97 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -2,6 +2,7 @@ package config import ( "errors" + "fmt" "net/http" "net/http/httptest" "os" @@ -41,3 +42,9 @@ func TestSetLoggerSetsLocalLogger(t *testing.T) { assert.NoError(t, err) assert.True(t, l.Core().Enabled(0)) } + +func TestSetLoggerCannotFindEnv(t *testing.T) { + l, err := setLogger("asdf") + assert.Equal(t, err, fmt.Errorf("cannot find ENV var so defaulting to debug level logging")) + assert.True(t, l.Core().Enabled(0)) +} From 5c5803a01ee11089e7222247687404765a4f1069 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Wed, 23 Aug 2023 20:00:52 -0700 Subject: [PATCH 05/15] add edge case check for negative page numbers --- api/handlers/call.go | 5 +---- api/handlers/civilian.go | 6 +++++- api/handlers/ems.go | 5 +---- api/handlers/emsVehicle.go | 5 +---- api/handlers/firearm.go | 5 +---- api/handlers/vehicle.go | 5 +---- 6 files changed, 10 insertions(+), 21 deletions(-) diff --git a/api/handlers/call.go b/api/handlers/call.go index 32ef54c..222c244 100644 --- a/api/handlers/call.go +++ b/api/handlers/call.go @@ -30,10 +30,7 @@ func (c Call) CallHandler(w http.ResponseWriter, r *http.Request) { zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) } limit64 := int64(Limit) - Page, err := strconv.Atoi(r.URL.Query().Get("page")) - if err != nil { - zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) - } + Page = getPage(Page, r) skip64 := int64(Page) dbResp, err := c.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { diff --git a/api/handlers/civilian.go b/api/handlers/civilian.go index 2b1c226..343d258 100644 --- a/api/handlers/civilian.go +++ b/api/handlers/civilian.go @@ -89,7 +89,7 @@ func (c Civilian) CiviliansByUserIDHandler(w http.ResponseWriter, r *http.Reques activeCommunityID := r.URL.Query().Get("active_community_id") Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) if err != nil { - zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) } limit64 := int64(Limit) Page = getPage(Page, r) @@ -153,6 +153,10 @@ func getPage(Page int, r *http.Request) int { if err != nil { zap.S().Errorf(fmt.Sprintf("error parsing page number: %v", err)) } + if Page < 1 { + zap.S().Warnf(fmt.Sprintf("cannot process page number less than 1. Got: %v", Page)) + return 1 + } } return Page } diff --git a/api/handlers/ems.go b/api/handlers/ems.go index a5fefbe..7379e60 100644 --- a/api/handlers/ems.go +++ b/api/handlers/ems.go @@ -30,10 +30,7 @@ func (e Ems) EmsHandler(w http.ResponseWriter, r *http.Request) { zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) } limit64 := int64(Limit) - Page, err := strconv.Atoi(r.URL.Query().Get("page")) - if err != nil { - zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) - } + Page = getPage(Page, r) skip64 := int64(Page) dbResp, err := e.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { diff --git a/api/handlers/emsVehicle.go b/api/handlers/emsVehicle.go index 89f9ff0..bee91d1 100644 --- a/api/handlers/emsVehicle.go +++ b/api/handlers/emsVehicle.go @@ -30,10 +30,7 @@ func (v EmsVehicle) EmsVehicleHandler(w http.ResponseWriter, r *http.Request) { zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) } limit64 := int64(Limit) - Page, err := strconv.Atoi(r.URL.Query().Get("page")) - if err != nil { - zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) - } + Page = getPage(Page, r) skip64 := int64(Page) dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { diff --git a/api/handlers/firearm.go b/api/handlers/firearm.go index f4d6bc4..de79e27 100644 --- a/api/handlers/firearm.go +++ b/api/handlers/firearm.go @@ -36,10 +36,7 @@ func (v Firearm) FirearmHandler(w http.ResponseWriter, r *http.Request) { zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) } limit64 := int64(Limit) - Page, err := strconv.Atoi(r.URL.Query().Get("page")) - if err != nil { - zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) - } + Page = getPage(Page, r) skip64 := int64(Page) dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { diff --git a/api/handlers/vehicle.go b/api/handlers/vehicle.go index 5bcd7d8..fa650a3 100644 --- a/api/handlers/vehicle.go +++ b/api/handlers/vehicle.go @@ -30,10 +30,7 @@ func (v Vehicle) VehicleHandler(w http.ResponseWriter, r *http.Request) { zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) } limit64 := int64(Limit) - Page, err := strconv.Atoi(r.URL.Query().Get("page")) - if err != nil { - zap.S().Warnf(fmt.Sprintf("page not set, using default of %v, err: %v", Page|1, err)) - } + Page = getPage(Page, r) skip64 := int64(Page) dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { From 71b1498f7e67eb0f7d91e4eae753a819d2deb776 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Thu, 24 Aug 2023 08:57:44 -0700 Subject: [PATCH 06/15] add pagination for firearm and vehicle routes --- api/handlers/call.go | 2 +- api/handlers/civilian.go | 10 +++++----- api/handlers/ems.go | 2 +- api/handlers/emsVehicle.go | 2 +- api/handlers/firearm.go | 15 +++++++++++---- api/handlers/vehicle.go | 15 +++++++++++---- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/api/handlers/call.go b/api/handlers/call.go index 222c244..98a705c 100644 --- a/api/handlers/call.go +++ b/api/handlers/call.go @@ -31,7 +31,7 @@ func (c Call) CallHandler(w http.ResponseWriter, r *http.Request) { } limit64 := int64(Limit) Page = getPage(Page, r) - skip64 := int64(Page) + skip64 := int64(Page * Limit) dbResp, err := c.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get calls", http.StatusNotFound, w, err) diff --git a/api/handlers/civilian.go b/api/handlers/civilian.go index 343d258..5aac661 100644 --- a/api/handlers/civilian.go +++ b/api/handlers/civilian.go @@ -20,7 +20,7 @@ import ( var ( // Page denotes the starting Page for pagination results - Page = 1 + Page = 0 ) // Civilian exported for testing purposes @@ -36,7 +36,7 @@ func (c Civilian) CivilianHandler(w http.ResponseWriter, r *http.Request) { } limit64 := int64(Limit) Page = getPage(Page, r) - skip64 := int64(Page) + skip64 := int64(Page * Limit) dbResp, err := c.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get civilians", http.StatusNotFound, w, err) @@ -93,7 +93,7 @@ func (c Civilian) CiviliansByUserIDHandler(w http.ResponseWriter, r *http.Reques } limit64 := int64(Limit) Page = getPage(Page, r) - skip64 := int64(Page) + skip64 := int64(Page * Limit) zap.S().Debugf("user_id: '%v'", userID) zap.S().Debugf("active_community: '%v'", activeCommunityID) @@ -153,9 +153,9 @@ func getPage(Page int, r *http.Request) int { if err != nil { zap.S().Errorf(fmt.Sprintf("error parsing page number: %v", err)) } - if Page < 1 { + if Page < 0 { zap.S().Warnf(fmt.Sprintf("cannot process page number less than 1. Got: %v", Page)) - return 1 + return 0 } } return Page diff --git a/api/handlers/ems.go b/api/handlers/ems.go index 7379e60..70f33bd 100644 --- a/api/handlers/ems.go +++ b/api/handlers/ems.go @@ -31,7 +31,7 @@ func (e Ems) EmsHandler(w http.ResponseWriter, r *http.Request) { } limit64 := int64(Limit) Page = getPage(Page, r) - skip64 := int64(Page) + skip64 := int64(Page * Limit) dbResp, err := e.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get ems", http.StatusNotFound, w, err) diff --git a/api/handlers/emsVehicle.go b/api/handlers/emsVehicle.go index bee91d1..f978082 100644 --- a/api/handlers/emsVehicle.go +++ b/api/handlers/emsVehicle.go @@ -31,7 +31,7 @@ func (v EmsVehicle) EmsVehicleHandler(w http.ResponseWriter, r *http.Request) { } limit64 := int64(Limit) Page = getPage(Page, r) - skip64 := int64(Page) + skip64 := int64(Page * Limit) dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get emsVehicles", http.StatusNotFound, w, err) diff --git a/api/handlers/firearm.go b/api/handlers/firearm.go index de79e27..53f6e27 100644 --- a/api/handlers/firearm.go +++ b/api/handlers/firearm.go @@ -37,7 +37,7 @@ func (v Firearm) FirearmHandler(w http.ResponseWriter, r *http.Request) { } limit64 := int64(Limit) Page = getPage(Page, r) - skip64 := int64(Page) + skip64 := int64(Page * Limit) dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get firearms", http.StatusNotFound, w, err) @@ -89,6 +89,13 @@ func (v Firearm) FirearmByIDHandler(w http.ResponseWriter, r *http.Request) { func (v Firearm) FirearmsByUserIDHandler(w http.ResponseWriter, r *http.Request) { userID := mux.Vars(r)["user_id"] activeCommunityID := r.URL.Query().Get("active_community_id") + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) zap.S().Debugf("user_id: '%v'", userID) zap.S().Debugf("active_community: '%v'", activeCommunityID) @@ -101,12 +108,12 @@ func (v Firearm) FirearmsByUserIDHandler(w http.ResponseWriter, r *http.Request) // // Likewise, if the user is not in a community, then we will display only the firearms // that are not in a community - var err error + err = nil if activeCommunityID != "" && activeCommunityID != "null" && activeCommunityID != "undefined" { dbResp, err = v.DB.Find(context.TODO(), bson.M{ "firearm.userID": userID, "firearm.activeCommunityID": activeCommunityID, - }) + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get firearms with active community id", http.StatusNotFound, w, err) return @@ -118,7 +125,7 @@ func (v Firearm) FirearmsByUserIDHandler(w http.ResponseWriter, r *http.Request) {"firearm.activeCommunityID": nil}, {"firearm.activeCommunityID": ""}, }, - }) + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get firearms with empty active community id", http.StatusNotFound, w, err) return diff --git a/api/handlers/vehicle.go b/api/handlers/vehicle.go index fa650a3..aec88d7 100644 --- a/api/handlers/vehicle.go +++ b/api/handlers/vehicle.go @@ -31,7 +31,7 @@ func (v Vehicle) VehicleHandler(w http.ResponseWriter, r *http.Request) { } limit64 := int64(Limit) Page = getPage(Page, r) - skip64 := int64(Page) + skip64 := int64(Page * Limit) dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get vehicles", http.StatusNotFound, w, err) @@ -82,6 +82,13 @@ func (v Vehicle) VehicleByIDHandler(w http.ResponseWriter, r *http.Request) { func (v Vehicle) VehiclesByUserIDHandler(w http.ResponseWriter, r *http.Request) { userID := mux.Vars(r)["user_id"] activeCommunityID := r.URL.Query().Get("active_community_id") + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) zap.S().Debugf("user_id: '%v'", userID) zap.S().Debugf("active_community: '%v'", activeCommunityID) @@ -94,12 +101,12 @@ func (v Vehicle) VehiclesByUserIDHandler(w http.ResponseWriter, r *http.Request) // // Likewise, if the user is not in a community, then we will display only the vehicles // that are not in a community - var err error + err = nil if activeCommunityID != "" && activeCommunityID != "null" && activeCommunityID != "undefined" { dbResp, err = v.DB.Find(context.TODO(), bson.M{ "vehicle.userID": userID, "vehicle.activeCommunityID": activeCommunityID, - }) + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get vehicles with active community id", http.StatusNotFound, w, err) return @@ -111,7 +118,7 @@ func (v Vehicle) VehiclesByUserIDHandler(w http.ResponseWriter, r *http.Request) {"vehicle.activeCommunityID": nil}, {"vehicle.activeCommunityID": ""}, }, - }) + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get vehicles with empty active community id", http.StatusNotFound, w, err) return From 29e0e446aaf1221488f6ba4244f0a391094ee7e2 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Thu, 24 Aug 2023 09:41:31 -0700 Subject: [PATCH 07/15] add test for negative page numbers --- api/handlers/civilian_test.go | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/api/handlers/civilian_test.go b/api/handlers/civilian_test.go index c92c341..d93d233 100644 --- a/api/handlers/civilian_test.go +++ b/api/handlers/civilian_test.go @@ -452,6 +452,54 @@ func TestCivilian_CivilianHandlerPaginationInvalidPageEntry(t *testing.T) { assert.Equal(t, "5fc51f36c72ff10004dca381", testCivilian[0].ID) } +func TestCivilian_CivilianHandlerPaginationInvalidPageEntryLessThan1(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians?limit=5&page=-1", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Civilian) + *arg = []models.Civilian{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CivilianHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testCivilian []models.Civilian + _ = json.Unmarshal(rr.Body.Bytes(), &testCivilian) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testCivilian[0].ID) +} + func TestCivilian_CivilianHandlerEmptyResponse(t *testing.T) { req, err := http.NewRequest("GET", "/api/v1/civilian", nil) if err != nil { From 4bd9c39934afb4aea00db09039777e8dd9b848e7 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Mon, 28 Aug 2023 16:59:50 -0700 Subject: [PATCH 08/15] add new routes for registered owners; update swagger docs with new routes --- api/handlers/api.go | 2 + api/handlers/firearm.go | 44 ++++++++ api/handlers/firearm_test.go | 191 +++++++++++++++++++++++++++++++++++ api/handlers/vehicle.go | 38 +++++++ api/handlers/vehicle_test.go | 191 +++++++++++++++++++++++++++++++++++ docs/docs.go | 24 +++++ docs/swagger.yaml | 22 +++- 7 files changed, 510 insertions(+), 2 deletions(-) diff --git a/api/handlers/api.go b/api/handlers/api.go index 25e3446..ff29c37 100644 --- a/api/handlers/api.go +++ b/api/handlers/api.go @@ -56,9 +56,11 @@ func (a *App) New() *mux.Router { apiCreate.Handle("/vehicle/{vehicle_id}", api.Middleware(http.HandlerFunc(v.VehicleByIDHandler))).Methods("GET") apiCreate.Handle("/vehicles", api.Middleware(http.HandlerFunc(v.VehicleHandler))).Methods("GET") apiCreate.Handle("/vehicles/user/{user_id}", api.Middleware(http.HandlerFunc(v.VehiclesByUserIDHandler))).Methods("GET") + apiCreate.Handle("/vehicles/registered-owner/{registered_owner_id}", api.Middleware(http.HandlerFunc(v.VehiclesByRegisteredOwnerIDHandler))).Methods("GET") apiCreate.Handle("/firearm/{firearm_id}", api.Middleware(http.HandlerFunc(f.FirearmByIDHandler))).Methods("GET") apiCreate.Handle("/firearms", api.Middleware(http.HandlerFunc(f.FirearmHandler))).Methods("GET") apiCreate.Handle("/firearms/user/{user_id}", api.Middleware(http.HandlerFunc(f.FirearmsByUserIDHandler))).Methods("GET") + apiCreate.Handle("/firearms/registered-owner/{registered_owner_id}", api.Middleware(http.HandlerFunc(f.FirearmsByRegisteredOwnerIDHandler))).Methods("GET") apiCreate.Handle("/ems/{ems_id}", api.Middleware(http.HandlerFunc(e.EmsByIDHandler))).Methods("GET") apiCreate.Handle("/ems", api.Middleware(http.HandlerFunc(e.EmsHandler))).Methods("GET") apiCreate.Handle("/ems/user/{user_id}", api.Middleware(http.HandlerFunc(e.EmsByUserIDHandler))).Methods("GET") diff --git a/api/handlers/firearm.go b/api/handlers/firearm.go index 53f6e27..097e5c4 100644 --- a/api/handlers/firearm.go +++ b/api/handlers/firearm.go @@ -145,3 +145,47 @@ func (v Firearm) FirearmsByUserIDHandler(w http.ResponseWriter, r *http.Request) w.WriteHeader(http.StatusOK) w.Write(b) } + +// FirearmsByRegisteredOwnerIDHandler returns all firearms that contain the given registeredOwnerID +func (v Firearm) FirearmsByRegisteredOwnerIDHandler(w http.ResponseWriter, r *http.Request) { + registeredOwnerID := mux.Vars(r)["registered_owner_id"] + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) + + zap.S().Debugf("registered_owner_id: '%v'", registeredOwnerID) + + var dbResp []models.Firearm + + // If the user is in a community then we want to search for firearms that + // are in that same community. This way each user can have different firearms + // across different communities. + // + // Likewise, if the user is not in a community, then we will display only the firearms + // that are not in a community + err = nil + dbResp, err = v.DB.Find(context.TODO(), bson.M{ + "firearm.registeredOwnerID": registeredOwnerID, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get firearms with empty registered owner id", http.StatusNotFound, w, err) + return + } + + // Because the frontend requires that the data elements inside models.Firearms exist, if + // len == 0 then we will just return an empty data object + if len(dbResp) == 0 { + dbResp = []models.Firearm{} + } + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} diff --git a/api/handlers/firearm_test.go b/api/handlers/firearm_test.go index 6431ca4..e7955c1 100644 --- a/api/handlers/firearm_test.go +++ b/api/handlers/firearm_test.go @@ -734,3 +734,194 @@ func TestFirearm_FirearmsByUserIDHandlerEmptyResponse(t *testing.T) { t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) } } + +func TestFirearm_FirearmsByRegisteredOwnerIDHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/firearms/registered-owner/1234", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Firearm) + *arg = []models.Firearm{{Details: models.FirearmDetails{CreatedAt: x}}} + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) + + firearmDatabase := databases.NewFirearmDatabase(db) + u := handlers.Firearm{ + DB: firearmDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.FirearmsByRegisteredOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestFirearm_FirearmsByRegisteredOwnerIDHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/firearms/registered-owner/1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) + + firearmDatabase := databases.NewFirearmDatabase(db) + u := handlers.Firearm{ + DB: firearmDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.FirearmsByRegisteredOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get firearms with empty registered owner id", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestFirearm_FirearmsByRegisteredOwnerIDHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/firearms/registered-owner/61be0ebf22cfea7e7550f00e", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Firearm) + *arg = []models.Firearm{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) + + firearmDatabase := databases.NewFirearmDatabase(db) + u := handlers.Firearm{ + DB: firearmDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.FirearmsByRegisteredOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testFirearm []models.Firearm + _ = json.Unmarshal(rr.Body.Bytes(), &testFirearm) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testFirearm[0].ID) +} + +func TestFirearm_FirearmsByRegisteredOwnerIDHandlerEmptyResponse(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/firearms/registered-owner/1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var cursorHelper databases.CursorHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + cursorHelper = &mocks.CursorHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + cursorHelper.(*mocks.CursorHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Firearm) + *arg = nil + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) + db.(*MockDatabaseHelper).On("Collection", "firearms").Return(conn) + + firearmDatabase := databases.NewFirearmDatabase(db) + u := handlers.Firearm{ + DB: firearmDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.FirearmsByRegisteredOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := "[]" + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} diff --git a/api/handlers/vehicle.go b/api/handlers/vehicle.go index aec88d7..09c6613 100644 --- a/api/handlers/vehicle.go +++ b/api/handlers/vehicle.go @@ -138,3 +138,41 @@ func (v Vehicle) VehiclesByUserIDHandler(w http.ResponseWriter, r *http.Request) w.WriteHeader(http.StatusOK) w.Write(b) } + +// VehiclesByRegisteredOwnerIDHandler returns all vehicles that contain the given registeredOwnerID +func (v Vehicle) VehiclesByRegisteredOwnerIDHandler(w http.ResponseWriter, r *http.Request) { + registeredOwnerID := mux.Vars(r)["registered_owner_id"] + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) + + zap.S().Debugf("registered_owner_id: '%v'", registeredOwnerID) + + var dbResp []models.Vehicle + + err = nil + dbResp, err = v.DB.Find(context.TODO(), bson.M{ + "vehicle.registeredOwnerID": registeredOwnerID, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get vehicles by registered owner id", http.StatusNotFound, w, err) + return + } + + // Because the frontend requires that the data elements inside models.Vehicles exist, if + // len == 0 then we will just return an empty data object + if len(dbResp) == 0 { + dbResp = []models.Vehicle{} + } + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} diff --git a/api/handlers/vehicle_test.go b/api/handlers/vehicle_test.go index bfff1ed..d01b277 100644 --- a/api/handlers/vehicle_test.go +++ b/api/handlers/vehicle_test.go @@ -734,3 +734,194 @@ func TestVehicle_VehiclesByUserIDHandlerEmptyResponse(t *testing.T) { t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) } } + +func TestVehicle_VehiclesByRegisteredOwnerIDHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/vehicles/registered-owner/1234", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Vehicle) + *arg = []models.Vehicle{{Details: models.VehicleDetails{CreatedAt: x}}} + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) + + vehicleDatabase := databases.NewVehicleDatabase(db) + u := handlers.Vehicle{ + DB: vehicleDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.VehiclesByRegisteredOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestVehicle_VehiclesByRegisteredOwnerIDHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/vehicles/registered-owner/1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) + + vehicleDatabase := databases.NewVehicleDatabase(db) + u := handlers.Vehicle{ + DB: vehicleDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.VehiclesByRegisteredOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get vehicles by registered owner id", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestVehicle_VehiclesByRegisteredOwnerIDHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/vehicles/registered-owner/61be0ebf22cfea7e7550f00e", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Vehicle) + *arg = []models.Vehicle{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) + + vehicleDatabase := databases.NewVehicleDatabase(db) + u := handlers.Vehicle{ + DB: vehicleDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.VehiclesByRegisteredOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testVehicle []models.Vehicle + _ = json.Unmarshal(rr.Body.Bytes(), &testVehicle) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testVehicle[0].ID) +} + +func TestVehicle_VehiclesByRegisteredOwnerIDHandlerEmptyResponse(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/vehicles/registered-owner/1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var cursorHelper databases.CursorHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + cursorHelper = &mocks.CursorHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + cursorHelper.(*mocks.CursorHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Vehicle) + *arg = nil + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) + db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) + + vehicleDatabase := databases.NewVehicleDatabase(db) + u := handlers.Vehicle{ + DB: vehicleDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.VehiclesByRegisteredOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := "[]" + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} diff --git a/docs/docs.go b/docs/docs.go index f6abdf7..4a9f07c 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -217,6 +217,18 @@ type vehiclesByUserIDParamsWrapper struct { ActiveCommunityID string `json:"active_community_id"` } +// swagger:route GET /api/v1/vehicles/registered-owner/{registered_owner_id} vehicle vehiclesByRegisteredOwnerID +// Get all vehicles by RegisteredOwnerID. +// responses: +// 200: vehiclesResponse + +// Shows all vehicles by RegisteredOwnerID +// swagger:response vehiclesResponse +type vehiclesByRegisteredOwnerIDResponseWrapper struct { + // in:body + Body []models.Vehicle +} + // swagger:route GET /api/v1/firearm/{firearm_id} firearm firearmByID // Get a firearm by ID. // responses: @@ -261,6 +273,18 @@ type firearmsByUserIDParamsWrapper struct { ActiveCommunityID string `json:"active_community_id"` } +// swagger:route GET /api/v1/firearms/registered-owner/{registered_owner_id} firearm firearmsByRegisteredOwnerID +// Get all firearms by RegisteredOwnerID. +// responses: +// 200: firearmsResponse + +// Shows all firearms by RegisteredOwnerID +// swagger:response firearmsResponse +type firearmsByRegisteredOwnerIDResponseWrapper struct { + // in:body + Body []models.Firearm +} + // swagger:route GET /api/v1/ems/{ems_id} ems emsByID // Get a ems by ID. // responses: diff --git a/docs/swagger.yaml b/docs/swagger.yaml index b487860..04b36fe 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -727,6 +727,15 @@ paths: summary: Get all firearms. tags: - firearm + /api/v1/firearms/registered-owner/{registered_owner_id}: + get: + operationId: firearmsByRegisteredOwnerID + responses: + "200": + $ref: '#/responses/firearmsResponse' + summary: Get all firearms by RegisteredOwnerID. + tags: + - firearm /api/v1/firearms/user/{user_id}: get: operationId: firearmsByUserID @@ -807,6 +816,15 @@ paths: summary: Get all vehicles. tags: - vehicle + /api/v1/vehicles/registered-owner/{registered_owner_id}: + get: + operationId: vehiclesByRegisteredOwnerID + responses: + "200": + $ref: '#/responses/vehiclesResponse' + summary: Get all vehicles by RegisteredOwnerID. + tags: + - vehicle /api/v1/vehicles/user/{user_id}: get: operationId: vehiclesByUserID @@ -897,7 +915,7 @@ responses: schema: $ref: '#/definitions/Firearm' firearmsResponse: - description: Shows all firearms by userID + description: Shows all firearms by RegisteredOwnerID schema: items: $ref: '#/definitions/Firearm' @@ -929,7 +947,7 @@ responses: schema: $ref: '#/definitions/Vehicle' vehiclesResponse: - description: Shows all vehicles by userID + description: Shows all vehicles by RegisteredOwnerID schema: items: $ref: '#/definitions/Vehicle' From aa546fb6cf5ca0faf7ba6105247947f805972c05 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Wed, 30 Aug 2023 14:36:03 -0700 Subject: [PATCH 09/15] add license routes and swagger docs --- api/handlers/api.go | 7 +- api/handlers/license.go | 191 ++++++ api/handlers/license_test.go | 927 +++++++++++++++++++++++++++++ databases/license.go | 47 ++ databases/license_test.go | 140 +++++ databases/mocks/LicenseDatabase.go | 78 +++ docs/docs.go | 56 ++ docs/swagger.yaml | 106 ++++ models/license.go | 23 + 9 files changed, 1574 insertions(+), 1 deletion(-) create mode 100644 api/handlers/license.go create mode 100644 api/handlers/license_test.go create mode 100644 databases/license.go create mode 100644 databases/license_test.go create mode 100644 databases/mocks/LicenseDatabase.go create mode 100644 models/license.go diff --git a/api/handlers/api.go b/api/handlers/api.go index ff29c37..1dae583 100644 --- a/api/handlers/api.go +++ b/api/handlers/api.go @@ -17,7 +17,7 @@ import ( "github.com/linesmerrill/police-cad-api/databases" ) -// App stores the router and db connection so it can be reused +// App stores the router and db connection, so it can be reused type App struct { Router *mux.Router DB databases.CollectionHelper @@ -35,6 +35,7 @@ func (a *App) New() *mux.Router { civ := Civilian{DB: databases.NewCivilianDatabase(a.dbHelper)} v := Vehicle{DB: databases.NewVehicleDatabase(a.dbHelper)} f := Firearm{DB: databases.NewFirearmDatabase(a.dbHelper)} + l := License{DB: databases.NewLicenseDatabase(a.dbHelper)} e := Ems{DB: databases.NewEmsDatabase(a.dbHelper)} ev := EmsVehicle{DB: databases.NewEmsVehicleDatabase(a.dbHelper)} call := Call{DB: databases.NewCallDatabase(a.dbHelper)} @@ -61,6 +62,10 @@ func (a *App) New() *mux.Router { apiCreate.Handle("/firearms", api.Middleware(http.HandlerFunc(f.FirearmHandler))).Methods("GET") apiCreate.Handle("/firearms/user/{user_id}", api.Middleware(http.HandlerFunc(f.FirearmsByUserIDHandler))).Methods("GET") apiCreate.Handle("/firearms/registered-owner/{registered_owner_id}", api.Middleware(http.HandlerFunc(f.FirearmsByRegisteredOwnerIDHandler))).Methods("GET") + apiCreate.Handle("/license/{license_id}", api.Middleware(http.HandlerFunc(l.LicenseByIDHandler))).Methods("GET") + apiCreate.Handle("/licenses", api.Middleware(http.HandlerFunc(l.LicenseHandler))).Methods("GET") + apiCreate.Handle("/licenses/user/{user_id}", api.Middleware(http.HandlerFunc(l.LicensesByUserIDHandler))).Methods("GET") + apiCreate.Handle("/licenses/owner/{owner_id}", api.Middleware(http.HandlerFunc(l.LicensesByOwnerIDHandler))).Methods("GET") apiCreate.Handle("/ems/{ems_id}", api.Middleware(http.HandlerFunc(e.EmsByIDHandler))).Methods("GET") apiCreate.Handle("/ems", api.Middleware(http.HandlerFunc(e.EmsHandler))).Methods("GET") apiCreate.Handle("/ems/user/{user_id}", api.Middleware(http.HandlerFunc(e.EmsByUserIDHandler))).Methods("GET") diff --git a/api/handlers/license.go b/api/handlers/license.go new file mode 100644 index 0000000..ea67a8b --- /dev/null +++ b/api/handlers/license.go @@ -0,0 +1,191 @@ +package handlers + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strconv" + + "github.com/gorilla/mux" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" + "go.uber.org/zap" + + "github.com/linesmerrill/police-cad-api/config" + "github.com/linesmerrill/police-cad-api/databases" + "github.com/linesmerrill/police-cad-api/models" +) + +// License exported for testing purposes +type License struct { + DB databases.LicenseDatabase +} + +// LicenseList paginated response with a list of items and next page id +type LicenseList struct { + Items []*models.License `json:"items"` + NextPageID int `json:"next_page_id,omitempty" example:"10"` +} + +// LicenseHandler returns all licenses +func (v License) LicenseHandler(w http.ResponseWriter, r *http.Request) { + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) + dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get licenses", http.StatusNotFound, w, err) + return + } + + // Because the frontend requires that the data elements inside models.Licenses exist, if + // len == 0 then we will just return an empty data object + if len(dbResp) == 0 { + dbResp = []models.License{} + } + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} + +// LicenseByIDHandler returns a license by ID +func (v License) LicenseByIDHandler(w http.ResponseWriter, r *http.Request) { + civID := mux.Vars(r)["license_id"] + + zap.S().Debugf("license_id: %v", civID) + + cID, err := primitive.ObjectIDFromHex(civID) + if err != nil { + config.ErrorStatus("failed to get objectID from Hex", http.StatusBadRequest, w, err) + return + } + + dbResp, err := v.DB.FindOne(context.Background(), bson.M{"_id": cID}) + if err != nil { + config.ErrorStatus("failed to get license by ID", http.StatusNotFound, w, err) + return + } + + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} + +// LicensesByUserIDHandler returns all licenses that contain the given userID +func (v License) LicensesByUserIDHandler(w http.ResponseWriter, r *http.Request) { + userID := mux.Vars(r)["user_id"] + activeCommunityID := r.URL.Query().Get("active_community_id") + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) + + zap.S().Debugf("user_id: '%v'", userID) + zap.S().Debugf("active_community: '%v'", activeCommunityID) + + var dbResp []models.License + + // If the user is in a community then we want to search for licenses that + // are in that same community. This way each user can have different licenses + // across different communities. + // + // Likewise, if the user is not in a community, then we will display only the licenses + // that are not in a community + err = nil + if activeCommunityID != "" && activeCommunityID != "null" && activeCommunityID != "undefined" { + dbResp, err = v.DB.Find(context.TODO(), bson.M{ + "license.userID": userID, + "license.activeCommunityID": activeCommunityID, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get licenses with active community id", http.StatusNotFound, w, err) + return + } + } else { + dbResp, err = v.DB.Find(context.TODO(), bson.M{ + "license.userID": userID, + "$or": []bson.M{ + {"license.activeCommunityID": nil}, + {"license.activeCommunityID": ""}, + }, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get licenses with empty active community id", http.StatusNotFound, w, err) + return + } + } + + // Because the frontend requires that the data elements inside models.Licenses exist, if + // len == 0 then we will just return an empty data object + if len(dbResp) == 0 { + dbResp = []models.License{} + } + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} + +// LicensesByOwnerIDHandler returns all licenses that contain the given OwnerID +func (v License) LicensesByOwnerIDHandler(w http.ResponseWriter, r *http.Request) { + ownerID := mux.Vars(r)["owner_id"] + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) + + zap.S().Debugf("owner_id: '%v'", ownerID) + + var dbResp []models.License + + // If the user is in a community then we want to search for licenses that + // are in that same community. This way each user can have different licenses + // across different communities. + // + // Likewise, if the user is not in a community, then we will display only the licenses + // that are not in a community + err = nil + dbResp, err = v.DB.Find(context.TODO(), bson.M{ + "license.ownerID": ownerID, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get licenses with empty owner id", http.StatusNotFound, w, err) + return + } + + // Because the frontend requires that the data elements inside models.Licenses exist, if + // len == 0 then we will just return an empty data object + if len(dbResp) == 0 { + dbResp = []models.License{} + } + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} diff --git a/api/handlers/license_test.go b/api/handlers/license_test.go new file mode 100644 index 0000000..f03c622 --- /dev/null +++ b/api/handlers/license_test.go @@ -0,0 +1,927 @@ +package handlers_test + +import ( + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gorilla/mux" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "github.com/linesmerrill/police-cad-api/api/handlers" + "github.com/linesmerrill/police-cad-api/databases" + "github.com/linesmerrill/police-cad-api/databases/mocks" + "github.com/linesmerrill/police-cad-api/models" +) + +func TestLicense_LicenseByIDHandler(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/license/1234", nil) + if err != nil { + t.Fatal(err) + } + + req = mux.SetURLVars(req, map[string]string{"license_id": "1234"}) + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mocked-error")) + conn.(*mocks.CollectionHelper).On("FindOne", mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicenseByIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusBadRequest { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get objectID from Hex", Error: "the provided hex string is not a valid ObjectID"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicenseByIDHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/license/5fc51f58c72ff10004dca382", nil) + if err != nil { + t.Fatal(err) + } + + req = mux.SetURLVars(req, map[string]string{"license_id": "5fc51f58c72ff10004dca382"}) + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(**models.License) + (*arg).Details.CreatedAt = x + + }) + conn.(*mocks.CollectionHelper).On("FindOne", mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicenseByIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicenseByIDHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/license/5fc51f58c72ff10004dca999", nil) + if err != nil { + t.Fatal(err) + } + + req = mux.SetURLVars(req, map[string]string{"license_id": "5fc51f58c72ff10004dca999"}) + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("FindOne", mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicenseByIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code:\ngot %v\nwant %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get license by ID", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicenseByIDHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/license/5fc51f58c72ff10004dca382", nil) + if err != nil { + t.Fatal(err) + } + + req = mux.SetURLVars(req, map[string]string{"license_id": "5fc51f58c72ff10004dca382"}) + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(**models.License) + (*arg).ID = "5fc51f58c72ff10004dca382" + + }) + conn.(*mocks.CollectionHelper).On("FindOne", mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicenseByIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + testLicense := models.License{} + _ = json.Unmarshal(rr.Body.Bytes(), &testLicense) + + assert.Equal(t, "5fc51f58c72ff10004dca382", testLicense.ID) +} + +func TestLicense_LicenseHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = []models.License{{Details: models.LicenseDetails{CreatedAt: x}}} + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicenseHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicenseHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicenseHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get licenses", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicenseHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = []models.License{{ID: "5fc51f58c72ff10004dca382"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicenseHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testLicense []models.License + _ = json.Unmarshal(rr.Body.Bytes(), &testLicense) + + assert.Equal(t, "5fc51f58c72ff10004dca382", testLicense[0].ID) +} + +func TestLicense_LicenseHandlerEmptyResponse(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/license", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var cursorHelper databases.CursorHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + cursorHelper = &mocks.CursorHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + cursorHelper.(*mocks.CursorHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = nil + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicenseHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := "[]" + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicensesByUserIDHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/user/1234", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = []models.License{{Details: models.LicenseDetails{CreatedAt: x}}} + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicensesByUserIDHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/user/1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get licenses with empty active community id", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicensesByUserIDHandlerActiveCommunityIDFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/user/1234?active_community_id=1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get licenses with active community id", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicensesByUserIDHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/user/61be0ebf22cfea7e7550f00e", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = []models.License{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testLicense []models.License + _ = json.Unmarshal(rr.Body.Bytes(), &testLicense) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testLicense[0].ID) +} + +func TestLicense_LicensesByUserIDHandlerSuccessWithActiveCommunityID(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/user/61be0ebf22cfea7e7550f00e?active_community_id=61c74b7b88e1abdac307bb39", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = []models.License{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testLicense []models.License + _ = json.Unmarshal(rr.Body.Bytes(), &testLicense) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testLicense[0].ID) +} + +func TestLicense_LicensesByUserIDHandlerSuccessWithNullCommunityID(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/user/61be0ebf22cfea7e7550f00e?active_community_id=null", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = []models.License{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testLicense []models.License + _ = json.Unmarshal(rr.Body.Bytes(), &testLicense) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testLicense[0].ID) +} + +func TestLicense_LicensesByUserIDHandlerEmptyResponse(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/user/1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var cursorHelper databases.CursorHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + cursorHelper = &mocks.CursorHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + cursorHelper.(*mocks.CursorHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = nil + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := "[]" + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicensesByOwnerIDHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/owner/1234", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = []models.License{{Details: models.LicenseDetails{CreatedAt: x}}} + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicensesByOwnerIDHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/owner/1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get licenses with empty owner id", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestLicense_LicensesByOwnerIDHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/owner/61be0ebf22cfea7e7550f00e", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = []models.License{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testLicense []models.License + _ = json.Unmarshal(rr.Body.Bytes(), &testLicense) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testLicense[0].ID) +} + +func TestLicense_LicensesByOwnerIDHandlerEmptyResponse(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/licenses/owner/1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var cursorHelper databases.CursorHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + cursorHelper = &mocks.CursorHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + cursorHelper.(*mocks.CursorHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = nil + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) + db.(*MockDatabaseHelper).On("Collection", "licenses").Return(conn) + + licenseDatabase := databases.NewLicenseDatabase(db) + u := handlers.License{ + DB: licenseDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.LicensesByOwnerIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := "[]" + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} diff --git a/databases/license.go b/databases/license.go new file mode 100644 index 0000000..d479842 --- /dev/null +++ b/databases/license.go @@ -0,0 +1,47 @@ +package databases + +// go generate: mockery --name LicenseDatabase + +import ( + "context" + + "github.com/linesmerrill/police-cad-api/models" + "go.mongodb.org/mongo-driver/mongo/options" +) + +const licenseName = "licenses" + +// LicenseDatabase contains the methods to use with the license database +type LicenseDatabase interface { + FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.License, error) + Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.License, error) +} + +type licenseDatabase struct { + db DatabaseHelper +} + +// NewLicenseDatabase initializes a new instance of license database with the provided db connection +func NewLicenseDatabase(db DatabaseHelper) LicenseDatabase { + return &licenseDatabase{ + db: db, + } +} + +func (c *licenseDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.License, error) { + license := &models.License{} + err := c.db.Collection(licenseName).FindOne(ctx, filter).Decode(&license) + if err != nil { + return nil, err + } + return license, nil +} + +func (c *licenseDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.License, error) { + var licenses []models.License + err := c.db.Collection(licenseName).Find(ctx, filter, opts...).Decode(&licenses) + if err != nil { + return nil, err + } + return licenses, nil +} diff --git a/databases/license_test.go b/databases/license_test.go new file mode 100644 index 0000000..877d994 --- /dev/null +++ b/databases/license_test.go @@ -0,0 +1,140 @@ +package databases_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "go.mongodb.org/mongo-driver/bson" + + "github.com/linesmerrill/police-cad-api/config" + "github.com/linesmerrill/police-cad-api/databases" + "github.com/linesmerrill/police-cad-api/databases/mocks" + "github.com/linesmerrill/police-cad-api/models" +) + +func TestNewLicenseDatabase(t *testing.T) { + _ = os.Setenv("DB_URI", "mongodb://127.0.0.1:27017") + _ = os.Setenv("DB_NAME", "test") + conf := config.New() + + dbClient, err := databases.NewClient(conf) + assert.NoError(t, err) + + db := databases.NewDatabase(conf, dbClient) + + userDB := databases.NewLicenseDatabase(db) + + assert.NotEmpty(t, userDB) +} + +func TestLicenseDatabase_FindOne(t *testing.T) { + + // define variables for interfaces + var dbHelper databases.DatabaseHelper + var collectionHelper databases.CollectionHelper + var srHelperErr databases.SingleResultHelper + var srHelperCorrect databases.SingleResultHelper + + // set interfaces implementation to mocked structures + dbHelper = &mocks.DatabaseHelper{} + collectionHelper = &mocks.CollectionHelper{} + srHelperErr = &mocks.SingleResultHelper{} + srHelperCorrect = &mocks.SingleResultHelper{} + + srHelperErr.(*mocks.SingleResultHelper). + On("Decode", mock.Anything). + Return(errors.New("mocked-error")) + + srHelperCorrect.(*mocks.SingleResultHelper). + On("Decode", mock.Anything). + Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(**models.License) + (*arg).ID = "mocked-user" + }) + + collectionHelper.(*mocks.CollectionHelper). + On("FindOne", context.Background(), bson.M{"error": true}). + Return(srHelperErr) + + collectionHelper.(*mocks.CollectionHelper). + On("FindOne", context.Background(), bson.M{"error": false}). + Return(srHelperCorrect) + + dbHelper.(*mocks.DatabaseHelper). + On("Collection", "licenses").Return(collectionHelper) + + // Create new database with mocked Database interface + userDba := databases.NewLicenseDatabase(dbHelper) + + // Call method with defined filter, that in our mocked function returns + // mocked-error + user, err := userDba.FindOne(context.Background(), bson.M{"error": true}) + + assert.Empty(t, user) + assert.EqualError(t, err, "mocked-error") + + // Now call the same function with different filter for correct + // result + user, err = userDba.FindOne(context.Background(), bson.M{"error": false}) + + assert.Equal(t, &models.License{ID: "mocked-user"}, user) + assert.NoError(t, err) +} + +func TestLicenseDatabase_Find(t *testing.T) { + + // define variables for interfaces + var dbHelper databases.DatabaseHelper + var collectionHelper databases.CollectionHelper + var srHelperErr databases.SingleResultHelper + var srHelperCorrect databases.SingleResultHelper + + // set interfaces implementation to mocked structures + dbHelper = &mocks.DatabaseHelper{} + collectionHelper = &mocks.CollectionHelper{} + srHelperErr = &mocks.SingleResultHelper{} + srHelperCorrect = &mocks.SingleResultHelper{} + + srHelperErr.(*mocks.SingleResultHelper). + On("Decode", mock.Anything). + Return(errors.New("mocked-error")) + + srHelperCorrect.(*mocks.SingleResultHelper). + On("Decode", mock.Anything). + Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.License) + *arg = []models.License{{ID: "mocked-user"}} + }) + + collectionHelper.(*mocks.CollectionHelper). + On("Find", context.Background(), bson.M{"error": true}). + Return(srHelperErr) + + collectionHelper.(*mocks.CollectionHelper). + On("Find", context.Background(), bson.M{"error": false}). + Return(srHelperCorrect) + + dbHelper.(*mocks.DatabaseHelper). + On("Collection", "licenses").Return(collectionHelper) + + // Create new database with mocked Database interface + userDba := databases.NewLicenseDatabase(dbHelper) + + // Call method with defined filter, that in our mocked function returns + // mocked-error + user, err := userDba.Find(context.Background(), bson.M{"error": true}) + + assert.Empty(t, user) + assert.EqualError(t, err, "mocked-error") + + // Now call the same function with different filter for correct + // result + user, err = userDba.Find(context.Background(), bson.M{"error": false}) + + assert.Equal(t, []models.License{{ID: "mocked-user"}}, user) + assert.NoError(t, err) +} diff --git a/databases/mocks/LicenseDatabase.go b/databases/mocks/LicenseDatabase.go new file mode 100644 index 0000000..a55ac9a --- /dev/null +++ b/databases/mocks/LicenseDatabase.go @@ -0,0 +1,78 @@ +// Code generated by mockery v2.10.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + + models "github.com/linesmerrill/police-cad-api/models" + + options "go.mongodb.org/mongo-driver/mongo/options" +) + +// LicenseDatabase is an autogenerated mock type for the LicenseDatabase type +type LicenseDatabase struct { + mock.Mock +} + +// Find provides a mock function with given fields: ctx, filter, opts +func (_m *LicenseDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.License, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, filter) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 []models.License + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOptions) []models.License); ok { + r0 = rf(ctx, filter, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.License) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOptions) error); ok { + r1 = rf(ctx, filter, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindOne provides a mock function with given fields: ctx, filter, opts +func (_m *LicenseDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.License, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, filter) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *models.License + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOneOptions) *models.License); ok { + r0 = rf(ctx, filter, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.License) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOneOptions) error); ok { + r1 = rf(ctx, filter, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/docs/docs.go b/docs/docs.go index 4a9f07c..fa013c3 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -285,6 +285,62 @@ type firearmsByRegisteredOwnerIDResponseWrapper struct { Body []models.Firearm } +// swagger:route GET /api/v1/license/{license_id} license licenseByID +// Get a license by ID. +// responses: +// 200: licenseByIDResponse +// 404: errorMessageResponse + +// Shows a license by the given license ID {license_id} +// swagger:response licenseByIDResponse +type licenseByIDResponseWrapper struct { + // in:body + Body models.License +} + +// swagger:route GET /api/v1/licenses license licenses +// Get all licenses. +// responses: +// 200: licensesResponse +// 404: errorMessageResponse + +// Shows all licenses. +// swagger:response licensesResponse +type licensesResponseWrapper struct { + // in:body + Body []models.License +} + +// swagger:route GET /api/v1/licenses/user/{user_id} license licensesByUserID +// Get all licenses by userID. +// responses: +// 200: licensesResponse + +// Shows all licenses by userID +// swagger:response licensesResponse +type licensesByUserIDResponseWrapper struct { + // in:body + Body []models.License +} + +// swagger:parameters licensesByUserID +type licensesByUserIDParamsWrapper struct { + // in:query + ActiveCommunityID string `json:"active_community_id"` +} + +// swagger:route GET /api/v1/licenses/owner/{owner_id} license licensesByOwnerID +// Get all licenses by OwnerID. +// responses: +// 200: licensesResponse + +// Shows all licenses by OwnerID +// swagger:response licensesResponse +type licensesByOwnerIDResponseWrapper struct { + // in:body + Body []models.License +} + // swagger:route GET /api/v1/ems/{ems_id} ems emsByID // Get a ems by ID. // responses: diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 04b36fe..61bc4a2 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -393,6 +393,57 @@ definitions: x-go-name: Alive type: object x-go-package: github.com/linesmerrill/police-cad-api/models + License: + description: License holds the structure for the license collection in mongo + properties: + __v: + format: int32 + type: integer + x-go-name: Version + _id: + type: string + x-go-name: ID + license: + $ref: '#/definitions/LicenseDetails' + type: object + x-go-package: github.com/linesmerrill/police-cad-api/models + LicenseDetails: + description: |- + LicenseDetails holds the structure for the inner user structure as + defined in the license collection in mongo + properties: + activeCommunityID: + type: string + x-go-name: ActiveCommunityID + additionalNotes: + type: string + x-go-name: AdditionalNotes + createdAt: + type: object + x-go-name: CreatedAt + expirationDate: + type: string + x-go-name: ExpirationDate + licenseType: + type: string + x-go-name: LicenseType + ownerID: + type: string + x-go-name: OwnerID + ownerName: + type: string + x-go-name: OwnerName + status: + type: string + x-go-name: Status + updatedAt: + type: object + x-go-name: UpdatedAt + userID: + type: string + x-go-name: UserID + type: object + x-go-package: github.com/linesmerrill/police-cad-api/models MessageError: description: MessageError contains the inner details for the error message response properties: @@ -750,6 +801,51 @@ paths: summary: Get all firearms by userID. tags: - firearm + /api/v1/license/{license_id}: + get: + operationId: licenseByID + responses: + "200": + $ref: '#/responses/licenseByIDResponse' + "404": + $ref: '#/responses/errorMessageResponse' + summary: Get a license by ID. + tags: + - license + /api/v1/licenses: + get: + operationId: licenses + responses: + "200": + $ref: '#/responses/licensesResponse' + "404": + $ref: '#/responses/errorMessageResponse' + summary: Get all licenses. + tags: + - license + /api/v1/licenses/owner/{owner_id}: + get: + operationId: licensesByOwnerID + responses: + "200": + $ref: '#/responses/licensesResponse' + summary: Get all licenses by OwnerID. + tags: + - license + /api/v1/licenses/user/{user_id}: + get: + operationId: licensesByUserID + parameters: + - in: query + name: active_community_id + type: string + x-go-name: ActiveCommunityID + responses: + "200": + $ref: '#/responses/licensesResponse' + summary: Get all licenses by userID. + tags: + - license /api/v1/name-search: get: operationId: nameSearchID @@ -925,6 +1021,16 @@ responses: means it is not. schema: $ref: '#/definitions/HealthCheckResponse' + licenseByIDResponse: + description: Shows a license by the given license ID {license_id} + schema: + $ref: '#/definitions/License' + licensesResponse: + description: Shows all licenses by OwnerID + schema: + items: + $ref: '#/definitions/License' + type: array nameSearchResponse: description: Shows a civilian by the given firstname, lastname, date-of-birth and communityID diff --git a/models/license.go b/models/license.go new file mode 100644 index 0000000..ade35ca --- /dev/null +++ b/models/license.go @@ -0,0 +1,23 @@ +package models + +// License holds the structure for the license collection in mongo +type License struct { + ID string `json:"_id" bson:"_id"` + Details LicenseDetails `json:"license" bson:"license"` + Version int32 `json:"__v" bson:"__v"` +} + +// LicenseDetails holds the structure for the inner user structure as +// defined in the license collection in mongo +type LicenseDetails struct { + LicenseType string `json:"licenseType" bson:"licenseType"` + Status string `json:"status" bson:"status"` + ExpirationDate string `json:"expirationDate" bson:"expirationDate"` + AdditionalNotes string `json:"additionalNotes" bson:"additionalNotes"` + OwnerID string `json:"ownerID" bson:"ownerID"` + OwnerName string `json:"ownerName" bson:"ownerName"` + ActiveCommunityID string `json:"activeCommunityID" bson:"activeCommunityID"` + UserID string `json:"userID" bson:"userID"` + CreatedAt interface{} `json:"createdAt" bson:"createdAt"` + UpdatedAt interface{} `json:"updatedAt" bson:"updatedAt"` +} From 9487f8bcbdaaad44c23b59bcb413bca45c1368c1 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Sat, 30 Sep 2023 11:51:45 -0700 Subject: [PATCH 10/15] update api with civilian name search routes --- api/handlers/api.go | 1 + api/handlers/civilian.go | 67 +++++++++++++++++++++++++++++++++++++ api/handlers/search/name.go | 6 ++-- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/api/handlers/api.go b/api/handlers/api.go index 1dae583..85e3d4b 100644 --- a/api/handlers/api.go +++ b/api/handlers/api.go @@ -53,6 +53,7 @@ func (a *App) New() *mux.Router { apiCreate.Handle("/civilian/{civilian_id}", api.Middleware(http.HandlerFunc(civ.CivilianByIDHandler))).Methods("GET") apiCreate.Handle("/civilians", api.Middleware(http.HandlerFunc(civ.CivilianHandler))).Methods("GET") apiCreate.Handle("/civilians/user/{user_id}", api.Middleware(http.HandlerFunc(civ.CiviliansByUserIDHandler))).Methods("GET") + apiCreate.Handle("/civilians/search", api.Middleware(http.HandlerFunc(civ.CiviliansByNameSearchHandler))).Methods("GET") apiCreate.Handle("/name-search", api.Middleware(http.HandlerFunc(n.NameSearchHandler))).Methods("GET") apiCreate.Handle("/vehicle/{vehicle_id}", api.Middleware(http.HandlerFunc(v.VehicleByIDHandler))).Methods("GET") apiCreate.Handle("/vehicles", api.Middleware(http.HandlerFunc(v.VehicleHandler))).Methods("GET") diff --git a/api/handlers/civilian.go b/api/handlers/civilian.go index 5aac661..fa04afb 100644 --- a/api/handlers/civilian.go +++ b/api/handlers/civilian.go @@ -144,6 +144,73 @@ func (c Civilian) CiviliansByUserIDHandler(w http.ResponseWriter, r *http.Reques w.Write(b) } +// CiviliansByNameSearchHandler returns paginated list of civilians that match the give name +func (c Civilian) CiviliansByNameSearchHandler(w http.ResponseWriter, r *http.Request) { + firstName := r.URL.Query().Get("first_name") + lastName := r.URL.Query().Get("last_name") + dateOfBirth := r.URL.Query().Get("date_of_birth") // optional + activeCommunityID := r.URL.Query().Get("active_community_id") // optional + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) + + zap.S().Debugf("first_name: '%v', last_name: '%v', date_of_birth: '%v'", firstName, lastName, dateOfBirth) + zap.S().Debugf("active_community: '%v'", activeCommunityID) + + var dbResp []models.Civilian + + // If the user is in a community then we want to search for civilians that + // are in that same community. This way each user can have different civilians + // across different communities. + // + // Likewise, if the user is not in a community, then we will display only the civilians + // that are not in a community + err = nil + if activeCommunityID != "" && activeCommunityID != "null" && activeCommunityID != "undefined" { + dbResp, err = c.DB.Find(context.TODO(), bson.M{ + "$text": bson.M{ + "$search": fmt.Sprintf("%s %s", firstName, lastName), + }, + "civilian.activeCommunityID": activeCommunityID, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get civilian name search with active community id", http.StatusNotFound, w, err) + return + } + } else { + dbResp, err = c.DB.Find(context.TODO(), bson.M{ + "civilian.firstName": firstName, + "civilian.lastName": lastName, + "civilian.birthday": dateOfBirth, + "$or": []bson.M{ + {"civilian.activeCommunityID": nil}, + {"civilian.activeCommunityID": ""}, + }, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get civilian name search with empty active community id", http.StatusNotFound, w, err) + return + } + } + + // Because the frontend requires that the data elements inside models.Civilians exist, if + // len == 0 then we will just return an empty data object + if len(dbResp) == 0 { + dbResp = []models.Civilian{} + } + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} + func getPage(Page int, r *http.Request) int { if r.URL.Query().Get("page") == "" { zap.S().Warnf("page not set, using default of %v", Page) diff --git a/api/handlers/search/name.go b/api/handlers/search/name.go index 621438f..dc10cac 100644 --- a/api/handlers/search/name.go +++ b/api/handlers/search/name.go @@ -26,10 +26,10 @@ func (n NameSearch) NameSearchHandler(w http.ResponseWriter, r *http.Request) { firstName := r.URL.Query().Get("first_name") lastName := r.URL.Query().Get("last_name") - dob := r.URL.Query().Get("dob") + dateOfBirth := r.URL.Query().Get("date_of_birth") communityID := r.URL.Query().Get("community_id") - zap.S().Debugf("first_name: %v, last_name: %v, dob: %v, community_id: %v", firstName, lastName, dob, communityID) + zap.S().Debugf("first_name: %v, last_name: %v, date_of_birth: %v, community_id: %v", firstName, lastName, dateOfBirth, communityID) dbResp, err := n.DB.Find(context.TODO(), bson.M{ "$and": []bson.M{ @@ -39,7 +39,7 @@ func (n NameSearch) NameSearchHandler(w http.ResponseWriter, r *http.Request) { }, }, bson.M{ - "civilian.birthday": dob, + "civilian.birthday": dateOfBirth, }, bson.M{"$or": []bson.M{ bson.M{"civilian.activeCommunityID": ""}, From 78a4d332de0ff86363ec7cd249937d9b02a442e1 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Sun, 1 Oct 2023 09:33:38 -0700 Subject: [PATCH 11/15] remove legacy name search; add warrant route and search --- api/handlers/api.go | 10 +- api/handlers/civilian_test.go | 377 +++++++++++++++ api/handlers/search/name.go | 66 --- api/handlers/search/name_test.go | 244 ---------- api/handlers/warrant.go | 132 ++++++ api/handlers/warrant_test.go | 736 +++++++++++++++++++++++++++++ databases/mocks/WarrantDatabase.go | 78 +++ databases/warrant.go | 47 ++ databases/warrant_test.go | 140 ++++++ docs/docs.go | 44 ++ models/warrant.go | 22 + 11 files changed, 1582 insertions(+), 314 deletions(-) delete mode 100644 api/handlers/search/name.go delete mode 100644 api/handlers/search/name_test.go create mode 100644 api/handlers/warrant.go create mode 100644 api/handlers/warrant_test.go create mode 100644 databases/mocks/WarrantDatabase.go create mode 100644 databases/warrant.go create mode 100644 databases/warrant_test.go create mode 100644 models/warrant.go diff --git a/api/handlers/api.go b/api/handlers/api.go index 85e3d4b..28a389f 100644 --- a/api/handlers/api.go +++ b/api/handlers/api.go @@ -5,8 +5,6 @@ import ( "io" "net/http" - "github.com/linesmerrill/police-cad-api/api/handlers/search" - "github.com/linesmerrill/police-cad-api/models" "github.com/gorilla/mux" @@ -31,13 +29,13 @@ func (a *App) New() *mux.Router { u := User{DB: databases.NewUserDatabase(a.dbHelper)} c := Community{DB: databases.NewCommunityDatabase(a.dbHelper)} - n := search.NameSearch{DB: databases.NewCivilianDatabase(a.dbHelper)} civ := Civilian{DB: databases.NewCivilianDatabase(a.dbHelper)} v := Vehicle{DB: databases.NewVehicleDatabase(a.dbHelper)} f := Firearm{DB: databases.NewFirearmDatabase(a.dbHelper)} l := License{DB: databases.NewLicenseDatabase(a.dbHelper)} e := Ems{DB: databases.NewEmsDatabase(a.dbHelper)} ev := EmsVehicle{DB: databases.NewEmsVehicleDatabase(a.dbHelper)} + w := Warrant{DB: databases.NewWarrantDatabase(a.dbHelper)} call := Call{DB: databases.NewCallDatabase(a.dbHelper)} // healthchex @@ -54,7 +52,6 @@ func (a *App) New() *mux.Router { apiCreate.Handle("/civilians", api.Middleware(http.HandlerFunc(civ.CivilianHandler))).Methods("GET") apiCreate.Handle("/civilians/user/{user_id}", api.Middleware(http.HandlerFunc(civ.CiviliansByUserIDHandler))).Methods("GET") apiCreate.Handle("/civilians/search", api.Middleware(http.HandlerFunc(civ.CiviliansByNameSearchHandler))).Methods("GET") - apiCreate.Handle("/name-search", api.Middleware(http.HandlerFunc(n.NameSearchHandler))).Methods("GET") apiCreate.Handle("/vehicle/{vehicle_id}", api.Middleware(http.HandlerFunc(v.VehicleByIDHandler))).Methods("GET") apiCreate.Handle("/vehicles", api.Middleware(http.HandlerFunc(v.VehicleHandler))).Methods("GET") apiCreate.Handle("/vehicles/user/{user_id}", api.Middleware(http.HandlerFunc(v.VehiclesByUserIDHandler))).Methods("GET") @@ -67,6 +64,11 @@ func (a *App) New() *mux.Router { apiCreate.Handle("/licenses", api.Middleware(http.HandlerFunc(l.LicenseHandler))).Methods("GET") apiCreate.Handle("/licenses/user/{user_id}", api.Middleware(http.HandlerFunc(l.LicensesByUserIDHandler))).Methods("GET") apiCreate.Handle("/licenses/owner/{owner_id}", api.Middleware(http.HandlerFunc(l.LicensesByOwnerIDHandler))).Methods("GET") + + apiCreate.Handle("/warrant/{warrant_id}", api.Middleware(http.HandlerFunc(w.WarrantByIDHandler))).Methods("GET") + apiCreate.Handle("/warrants", api.Middleware(http.HandlerFunc(w.WarrantHandler))).Methods("GET") + apiCreate.Handle("/warrants/user/{user_id}", api.Middleware(http.HandlerFunc(w.WarrantsByUserIDHandler))).Methods("GET") + apiCreate.Handle("/ems/{ems_id}", api.Middleware(http.HandlerFunc(e.EmsByIDHandler))).Methods("GET") apiCreate.Handle("/ems", api.Middleware(http.HandlerFunc(e.EmsHandler))).Methods("GET") apiCreate.Handle("/ems/user/{user_id}", api.Middleware(http.HandlerFunc(e.EmsByUserIDHandler))).Methods("GET") diff --git a/api/handlers/civilian_test.go b/api/handlers/civilian_test.go index d93d233..c5863cb 100644 --- a/api/handlers/civilian_test.go +++ b/api/handlers/civilian_test.go @@ -878,3 +878,380 @@ func TestCivilian_CiviliansByUserIDHandlerEmptyResponse(t *testing.T) { t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) } } + +func TestCivilian_CiviliansByNameSearchHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians/search?active_community_id=61c74b7b88e1abdac307bb39&first_name=alessandro&last_name=mills&date_of_birth=1987-03-20", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Civilian) + *arg = []models.Civilian{{Details: models.CivilianDetails{CreatedAt: x}}} + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CiviliansByNameSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestCivilian_CiviliansByNameSearchHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians/search?active_community_id=61c74b7b88e1abdac307bb39&first_name=alessandro&last_name=mills&date_of_birth=1987-03-20", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CiviliansByNameSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get civilian name search with active community id", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestCivilian_CiviliansByNameSearchHandlerActiveCommunityIDFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians/search?active_community_id=61c74b7b88e1abdac307bb39&first_name=alessandro&last_name=mills&date_of_birth=1987-03-20", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CiviliansByNameSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get civilian name search with active community id", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestCivilian_CiviliansByNameSearchHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians/search?active_community_id=61c74b7b88e1abdac307bb39&first_name=alessandro&last_name=mills&date_of_birth=1987-03-20", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Civilian) + *arg = []models.Civilian{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CiviliansByNameSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testCivilian []models.Civilian + _ = json.Unmarshal(rr.Body.Bytes(), &testCivilian) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testCivilian[0].ID) +} + +func TestCivilian_CiviliansByNameSearchHandlerSuccessWithActiveCommunityID(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians/search?active_community_id=61c74b7b88e1abdac307bb39&first_name=alessandro&last_name=mills&date_of_birth=1987-03-20", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Civilian) + *arg = []models.Civilian{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CiviliansByNameSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testCivilian []models.Civilian + _ = json.Unmarshal(rr.Body.Bytes(), &testCivilian) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testCivilian[0].ID) +} + +func TestCivilian_CiviliansByNameSearchHandlerSuccessWithNullCommunityID(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians/search?active_community_id=null&first_name=alessandro&last_name=mills&date_of_birth=1987-03-20", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Civilian) + *arg = []models.Civilian{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CiviliansByNameSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testCivilian []models.Civilian + _ = json.Unmarshal(rr.Body.Bytes(), &testCivilian) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testCivilian[0].ID) +} + +func TestCivilian_CiviliansByNameSearchHandlerFailedToFindOneWithEmptyCommunityID(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians/search?active_community_id=null&first_name=alessandro&last_name=mills&date_of_birth=1987-03-20", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CiviliansByNameSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get civilian name search with empty active community id", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestCivilian_CiviliansByNameSearchHandlerEmptyResponse(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/civilians/search?active_community_id=61c74b7b88e1abdac307bb39&first_name=alessandro&last_name=mills&date_of_birth=1987-03-20", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var cursorHelper databases.CursorHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + cursorHelper = &mocks.CursorHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + cursorHelper.(*mocks.CursorHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Civilian) + *arg = nil + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) + db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) + + civilianDatabase := databases.NewCivilianDatabase(db) + u := handlers.Civilian{ + DB: civilianDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.CiviliansByNameSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := "[]" + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} diff --git a/api/handlers/search/name.go b/api/handlers/search/name.go deleted file mode 100644 index dc10cac..0000000 --- a/api/handlers/search/name.go +++ /dev/null @@ -1,66 +0,0 @@ -package search - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - - "go.mongodb.org/mongo-driver/bson" - "go.uber.org/zap" - - "github.com/linesmerrill/police-cad-api/config" - "github.com/linesmerrill/police-cad-api/databases" - "github.com/linesmerrill/police-cad-api/models" -) - -// NameSearch holds the database object -type NameSearch struct { - DB databases.CivilianDatabase -} - -// NameSearchHandler contains the logic to handle basic name searches given -// a first name, last name, date-of-birth and communityID. This is the main -// search route for trying to locate a name in the database. -func (n NameSearch) NameSearchHandler(w http.ResponseWriter, r *http.Request) { - - firstName := r.URL.Query().Get("first_name") - lastName := r.URL.Query().Get("last_name") - dateOfBirth := r.URL.Query().Get("date_of_birth") - communityID := r.URL.Query().Get("community_id") - - zap.S().Debugf("first_name: %v, last_name: %v, date_of_birth: %v, community_id: %v", firstName, lastName, dateOfBirth, communityID) - - dbResp, err := n.DB.Find(context.TODO(), bson.M{ - "$and": []bson.M{ - bson.M{ - "$text": bson.M{ - "$search": fmt.Sprintf("%v %v", firstName, lastName), - }, - }, - bson.M{ - "civilian.birthday": dateOfBirth, - }, - bson.M{"$or": []bson.M{ - bson.M{"civilian.activeCommunityID": ""}, - bson.M{"civilian.activeCommunityID": nil}, - }}, - }, - }) - if err != nil { - config.ErrorStatus("failed to get name", http.StatusNotFound, w, err) - return - } - // Because the frontend requires that the data elements inside models.User exist, if - // len == 0 then we will just return an empty data object - if len(dbResp) == 0 { - dbResp = []models.Civilian{} - } - b, err := json.Marshal(dbResp) - if err != nil { - config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) - return - } - w.WriteHeader(http.StatusOK) - w.Write(b) -} diff --git a/api/handlers/search/name_test.go b/api/handlers/search/name_test.go deleted file mode 100644 index 066db80..0000000 --- a/api/handlers/search/name_test.go +++ /dev/null @@ -1,244 +0,0 @@ -package search_test - -import ( - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "testing" - - "github.com/linesmerrill/police-cad-api/api/handlers/search" - - "github.com/linesmerrill/police-cad-api/databases" - "github.com/linesmerrill/police-cad-api/databases/mocks" - "github.com/linesmerrill/police-cad-api/models" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -type MockDatabaseHelper struct { - mock.Mock -} - -// Client provides a mock function. -func (_m *MockDatabaseHelper) Client() databases.ClientHelper { - ret := _m.Called() - - var r0 databases.ClientHelper - if rf, ok := ret.Get(0).(func() databases.ClientHelper); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(databases.ClientHelper) - } - } - - return r0 -} - -// Collection provides a mock function. -func (_m *MockDatabaseHelper) Collection(name string) databases.CollectionHelper { - ret := _m.Called(name) - - var r0 databases.CollectionHelper - if rf, ok := ret.Get(0).(func(string) databases.CollectionHelper); ok { - r0 = rf(name) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(databases.CollectionHelper) - } - } - - return r0 -} - -func TestName_NameSearchHandlerJsonMarshalError(t *testing.T) { - req, err := http.NewRequest("GET", "/api/v1/name-search?first_name=John&last_name=Yelp", nil) - if err != nil { - t.Fatal(err) - } - req.Header.Set("Authorization", "Bearer abc123") - - var db databases.DatabaseHelper - var client databases.ClientHelper - var conn databases.CollectionHelper - var singleResultHelper databases.SingleResultHelper - - db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} - client = &mocks.ClientHelper{} - conn = &mocks.CollectionHelper{} - singleResultHelper = &mocks.SingleResultHelper{} - - x := map[string]interface{}{ - "foo": make(chan int), - } - - client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) - db.(*MockDatabaseHelper).On("Client").Return(client) - singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { - arg := args.Get(0).(*[]models.Civilian) - *arg = []models.Civilian{{Details: models.CivilianDetails{CreatedAt: x}}} - }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) - db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) - - civilianDatabase := databases.NewCivilianDatabase(db) - u := search.NameSearch{ - DB: civilianDatabase, - } - - rr := httptest.NewRecorder() - handler := http.HandlerFunc(u.NameSearchHandler) - - handler.ServeHTTP(rr, req) - - if status := rr.Code; status != http.StatusInternalServerError { - t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) - } - - expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} - b, _ := json.Marshal(expected) - if rr.Body.String() != string(b) { - t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) - } -} - -func TestName_NameSearchHandlerFailedToFindOne(t *testing.T) { - req, err := http.NewRequest("GET", "/api/v1/name-search?first_name=Does-not-exist&last_name=Does-not-exist", nil) - if err != nil { - t.Fatal(err) - } - - req.Header.Set("Authorization", "Bearer abc123") - - var db databases.DatabaseHelper - var client databases.ClientHelper - var conn databases.CollectionHelper - var singleResultHelper databases.SingleResultHelper - - db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} - client = &mocks.ClientHelper{} - conn = &mocks.CollectionHelper{} - singleResultHelper = &mocks.SingleResultHelper{} - - client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) - db.(*MockDatabaseHelper).On("Client").Return(client) - singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) - db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) - - civilianDatabase := databases.NewCivilianDatabase(db) - u := search.NameSearch{ - DB: civilianDatabase, - } - - rr := httptest.NewRecorder() - handler := http.HandlerFunc(u.NameSearchHandler) - - handler.ServeHTTP(rr, req) - - if status := rr.Code; status != http.StatusNotFound { - t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) - } - - expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get name", Error: "mongo: no documents in result"}} - b, _ := json.Marshal(expected) - if rr.Body.String() != string(b) { - t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) - } -} - -func TestName_NameSearchHandlerSuccess(t *testing.T) { - req, err := http.NewRequest("GET", "/api/v1/name-search?first_name=John&last_name=Yelp", nil) - if err != nil { - t.Fatal(err) - } - - req.Header.Set("Authorization", "Bearer abc123") - - var db databases.DatabaseHelper - var client databases.ClientHelper - var conn databases.CollectionHelper - var singleResultHelper databases.SingleResultHelper - - db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} - client = &mocks.ClientHelper{} - conn = &mocks.CollectionHelper{} - singleResultHelper = &mocks.SingleResultHelper{} - - client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) - db.(*MockDatabaseHelper).On("Client").Return(client) - singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { - arg := args.Get(0).(*[]models.Civilian) - *arg = []models.Civilian{{ID: "5fc51f58c72ff10004dca382"}} - - }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) - db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) - - civilianDatabase := databases.NewCivilianDatabase(db) - u := search.NameSearch{ - DB: civilianDatabase, - } - - rr := httptest.NewRecorder() - handler := http.HandlerFunc(u.NameSearchHandler) - - handler.ServeHTTP(rr, req) - - if status := rr.Code; status != http.StatusOK { - t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) - } - - var testCivilian []models.Civilian - _ = json.Unmarshal(rr.Body.Bytes(), &testCivilian) - - assert.Equal(t, "5fc51f58c72ff10004dca382", testCivilian[0].ID) -} - -func TestName_NameSearchHandlerEmptyResponse(t *testing.T) { - req, err := http.NewRequest("GET", "/api/v1/name-search?first_name=Empty&last_name=Response", nil) - if err != nil { - t.Fatal(err) - } - - req.Header.Set("Authorization", "Bearer abc123") - - var db databases.DatabaseHelper - var client databases.ClientHelper - var conn databases.CollectionHelper - var cursorHelper databases.CursorHelper - - db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} - client = &mocks.ClientHelper{} - conn = &mocks.CollectionHelper{} - cursorHelper = &mocks.CursorHelper{} - - client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) - db.(*MockDatabaseHelper).On("Client").Return(client) - cursorHelper.(*mocks.CursorHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { - arg := args.Get(0).(*[]models.Civilian) - *arg = nil - }) - conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) - db.(*MockDatabaseHelper).On("Collection", "civilians").Return(conn) - - civilianDatabase := databases.NewCivilianDatabase(db) - u := search.NameSearch{ - DB: civilianDatabase, - } - - rr := httptest.NewRecorder() - handler := http.HandlerFunc(u.NameSearchHandler) - - handler.ServeHTTP(rr, req) - - if status := rr.Code; status != http.StatusOK { - t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) - } - - expected := "[]" - if rr.Body.String() != expected { - t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) - } -} diff --git a/api/handlers/warrant.go b/api/handlers/warrant.go new file mode 100644 index 0000000..fbdda9c --- /dev/null +++ b/api/handlers/warrant.go @@ -0,0 +1,132 @@ +package handlers + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strconv" + + "github.com/gorilla/mux" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" + "go.uber.org/zap" + + "github.com/linesmerrill/police-cad-api/config" + "github.com/linesmerrill/police-cad-api/databases" + "github.com/linesmerrill/police-cad-api/models" +) + +// Warrant exported for testing purposes +type Warrant struct { + DB databases.WarrantDatabase +} + +// WarrantList paginated response with a list of items and next page id +type WarrantList struct { + Items []*models.Warrant `json:"items"` + NextPageID int `json:"next_page_id,omitempty" example:"10"` +} + +// WarrantHandler returns all warrants +func (v Warrant) WarrantHandler(w http.ResponseWriter, r *http.Request) { + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v, err: %v", Limit|10, err)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) + dbResp, err := v.DB.Find(context.TODO(), bson.D{}, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get warrants", http.StatusNotFound, w, err) + return + } + + // Because the frontend requires that the data elements inside models.Warrants exist, if + // len == 0 then we will just return an empty data object + if len(dbResp) == 0 { + dbResp = []models.Warrant{} + } + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} + +// WarrantByIDHandler returns a warrant by ID +func (v Warrant) WarrantByIDHandler(w http.ResponseWriter, r *http.Request) { + civID := mux.Vars(r)["warrant_id"] + + zap.S().Debugf("warrant_id: %v", civID) + + cID, err := primitive.ObjectIDFromHex(civID) + if err != nil { + config.ErrorStatus("failed to get objectID from Hex", http.StatusBadRequest, w, err) + return + } + + dbResp, err := v.DB.FindOne(context.Background(), bson.M{"_id": cID}) + if err != nil { + config.ErrorStatus("failed to get warrant by ID", http.StatusNotFound, w, err) + return + } + + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} + +// WarrantsByUserIDHandler returns all warrants that contain the given userID +func (v Warrant) WarrantsByUserIDHandler(w http.ResponseWriter, r *http.Request) { + userID := mux.Vars(r)["user_id"] + activeCommunityID := r.URL.Query().Get("active_community_id") + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) + + zap.S().Debugf("user_id: '%v'", userID) + zap.S().Debugf("active_community: '%v'", activeCommunityID) + + var dbResp []models.Warrant + + // If the user is in a community then we want to search for warrants that + // are in that same community. This way each user can have different warrants + // across different communities. + // + // Likewise, if the user is not in a community, then we will display only the warrants + // that are not in a community + err = nil + dbResp, err = v.DB.Find(context.TODO(), bson.M{ + "warrant.accusedID": userID, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get warrants", http.StatusNotFound, w, err) + return + } + + // Because the frontend requires that the data elements inside models.Warrants exist, if + // len == 0 then we will just return an empty data object + if len(dbResp) == 0 { + dbResp = []models.Warrant{} + } + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} diff --git a/api/handlers/warrant_test.go b/api/handlers/warrant_test.go new file mode 100644 index 0000000..e15f24f --- /dev/null +++ b/api/handlers/warrant_test.go @@ -0,0 +1,736 @@ +package handlers_test + +import ( + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gorilla/mux" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "github.com/linesmerrill/police-cad-api/api/handlers" + "github.com/linesmerrill/police-cad-api/databases" + "github.com/linesmerrill/police-cad-api/databases/mocks" + "github.com/linesmerrill/police-cad-api/models" +) + +func TestWarrant_WarrantByIDHandler(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrant/1234", nil) + if err != nil { + t.Fatal(err) + } + + req = mux.SetURLVars(req, map[string]string{"warrant_id": "1234"}) + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mocked-error")) + conn.(*mocks.CollectionHelper).On("FindOne", mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantByIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusBadRequest { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get objectID from Hex", Error: "the provided hex string is not a valid ObjectID"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} + +func TestWarrant_WarrantByIDHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrant/5fc51f58c72ff10004dca382", nil) + if err != nil { + t.Fatal(err) + } + + req = mux.SetURLVars(req, map[string]string{"warrant_id": "5fc51f58c72ff10004dca382"}) + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(**models.Warrant) + (*arg).Details.CreatedAt = x + + }) + conn.(*mocks.CollectionHelper).On("FindOne", mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantByIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} + +func TestWarrant_WarrantByIDHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrant/5fc51f58c72ff10004dca999", nil) + if err != nil { + t.Fatal(err) + } + + req = mux.SetURLVars(req, map[string]string{"warrant_id": "5fc51f58c72ff10004dca999"}) + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("FindOne", mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantByIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code:\ngot %v\nwant %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get warrant by ID", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestWarrant_WarrantByIDHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrant/5fc51f58c72ff10004dca382", nil) + if err != nil { + t.Fatal(err) + } + + req = mux.SetURLVars(req, map[string]string{"warrant_id": "5fc51f58c72ff10004dca382"}) + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(**models.Warrant) + (*arg).ID = "5fc51f58c72ff10004dca382" + + }) + conn.(*mocks.CollectionHelper).On("FindOne", mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantByIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + testWarrant := models.Warrant{} + _ = json.Unmarshal(rr.Body.Bytes(), &testWarrant) + + assert.Equal(t, "5fc51f58c72ff10004dca382", testWarrant.ID) +} + +func TestWarrant_WarrantHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Warrant) + *arg = []models.Warrant{{Details: models.WarrantDetails{CreatedAt: x}}} + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestWarrant_WarrantHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get warrants", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestWarrant_WarrantHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Warrant) + *arg = []models.Warrant{{ID: "5fc51f58c72ff10004dca382"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testWarrant []models.Warrant + _ = json.Unmarshal(rr.Body.Bytes(), &testWarrant) + + assert.Equal(t, "5fc51f58c72ff10004dca382", testWarrant[0].ID) +} + +func TestWarrant_WarrantHandlerEmptyResponse(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrant", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var cursorHelper databases.CursorHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + cursorHelper = &mocks.CursorHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + cursorHelper.(*mocks.CursorHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Warrant) + *arg = nil + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := "[]" + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} + +func TestWarrant_WarrantsByUserIDHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants/user/1234", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Warrant) + *arg = []models.Warrant{{Details: models.WarrantDetails{CreatedAt: x}}} + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantsByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestWarrant_WarrantsByUserIDHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants/user/1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantsByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get warrants", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestWarrant_WarrantsByUserIDHandlerActiveCommunityIDFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants/user/1234?active_community_id=1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("mongo: no documents in result")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantsByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get warrants", Error: "mongo: no documents in result"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestWarrant_WarrantsByUserIDHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants/user/61be0ebf22cfea7e7550f00e", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Warrant) + *arg = []models.Warrant{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantsByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testWarrant []models.Warrant + _ = json.Unmarshal(rr.Body.Bytes(), &testWarrant) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testWarrant[0].ID) +} + +func TestWarrant_WarrantsByUserIDHandlerSuccessWithActiveCommunityID(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants/user/61be0ebf22cfea7e7550f00e?active_community_id=61c74b7b88e1abdac307bb39", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Warrant) + *arg = []models.Warrant{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantsByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testWarrant []models.Warrant + _ = json.Unmarshal(rr.Body.Bytes(), &testWarrant) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testWarrant[0].ID) +} + +func TestWarrant_WarrantsByUserIDHandlerSuccessWithNullCommunityID(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants/user/61be0ebf22cfea7e7550f00e?active_community_id=null", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Warrant) + *arg = []models.Warrant{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantsByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testWarrant []models.Warrant + _ = json.Unmarshal(rr.Body.Bytes(), &testWarrant) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testWarrant[0].ID) +} + +func TestWarrant_WarrantsByUserIDHandlerEmptyResponse(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants/user/1234", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var cursorHelper databases.CursorHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + cursorHelper = &mocks.CursorHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + cursorHelper.(*mocks.CursorHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Warrant) + *arg = nil + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantsByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := "[]" + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} diff --git a/databases/mocks/WarrantDatabase.go b/databases/mocks/WarrantDatabase.go new file mode 100644 index 0000000..2cf092f --- /dev/null +++ b/databases/mocks/WarrantDatabase.go @@ -0,0 +1,78 @@ +// Code generated by mockery v2.10.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + + models "github.com/linesmerrill/police-cad-api/models" + + options "go.mongodb.org/mongo-driver/mongo/options" +) + +// WarrantDatabase is an autogenerated mock type for the WarrantDatabase type +type WarrantDatabase struct { + mock.Mock +} + +// Find provides a mock function with given fields: ctx, filter, opts +func (_m *WarrantDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.Warrant, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, filter) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 []models.Warrant + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOptions) []models.Warrant); ok { + r0 = rf(ctx, filter, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Warrant) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOptions) error); ok { + r1 = rf(ctx, filter, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindOne provides a mock function with given fields: ctx, filter, opts +func (_m *WarrantDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.Warrant, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, filter) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *models.Warrant + if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...*options.FindOneOptions) *models.Warrant); ok { + r0 = rf(ctx, filter, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.Warrant) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, interface{}, ...*options.FindOneOptions) error); ok { + r1 = rf(ctx, filter, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/databases/warrant.go b/databases/warrant.go new file mode 100644 index 0000000..1415a15 --- /dev/null +++ b/databases/warrant.go @@ -0,0 +1,47 @@ +package databases + +// go generate: mockery --name WarrantDatabase + +import ( + "context" + + "github.com/linesmerrill/police-cad-api/models" + "go.mongodb.org/mongo-driver/mongo/options" +) + +const warrantName = "warrants" + +// WarrantDatabase contains the methods to use with the warrant database +type WarrantDatabase interface { + FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.Warrant, error) + Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.Warrant, error) +} + +type warrantDatabase struct { + db DatabaseHelper +} + +// NewWarrantDatabase initializes a new instance of warrant database with the provided db connection +func NewWarrantDatabase(db DatabaseHelper) WarrantDatabase { + return &warrantDatabase{ + db: db, + } +} + +func (c *warrantDatabase) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) (*models.Warrant, error) { + warrant := &models.Warrant{} + err := c.db.Collection(warrantName).FindOne(ctx, filter).Decode(&warrant) + if err != nil { + return nil, err + } + return warrant, nil +} + +func (c *warrantDatabase) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) ([]models.Warrant, error) { + var warrants []models.Warrant + err := c.db.Collection(warrantName).Find(ctx, filter, opts...).Decode(&warrants) + if err != nil { + return nil, err + } + return warrants, nil +} diff --git a/databases/warrant_test.go b/databases/warrant_test.go new file mode 100644 index 0000000..6adfb57 --- /dev/null +++ b/databases/warrant_test.go @@ -0,0 +1,140 @@ +package databases_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "go.mongodb.org/mongo-driver/bson" + + "github.com/linesmerrill/police-cad-api/config" + "github.com/linesmerrill/police-cad-api/databases" + "github.com/linesmerrill/police-cad-api/databases/mocks" + "github.com/linesmerrill/police-cad-api/models" +) + +func TestNewWarrantDatabase(t *testing.T) { + _ = os.Setenv("DB_URI", "mongodb://127.0.0.1:27017") + _ = os.Setenv("DB_NAME", "test") + conf := config.New() + + dbClient, err := databases.NewClient(conf) + assert.NoError(t, err) + + db := databases.NewDatabase(conf, dbClient) + + userDB := databases.NewWarrantDatabase(db) + + assert.NotEmpty(t, userDB) +} + +func TestWarrantDatabase_FindOne(t *testing.T) { + + // define variables for interfaces + var dbHelper databases.DatabaseHelper + var collectionHelper databases.CollectionHelper + var srHelperErr databases.SingleResultHelper + var srHelperCorrect databases.SingleResultHelper + + // set interfaces implementation to mocked structures + dbHelper = &mocks.DatabaseHelper{} + collectionHelper = &mocks.CollectionHelper{} + srHelperErr = &mocks.SingleResultHelper{} + srHelperCorrect = &mocks.SingleResultHelper{} + + srHelperErr.(*mocks.SingleResultHelper). + On("Decode", mock.Anything). + Return(errors.New("mocked-error")) + + srHelperCorrect.(*mocks.SingleResultHelper). + On("Decode", mock.Anything). + Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(**models.Warrant) + (*arg).ID = "mocked-user" + }) + + collectionHelper.(*mocks.CollectionHelper). + On("FindOne", context.Background(), bson.M{"error": true}). + Return(srHelperErr) + + collectionHelper.(*mocks.CollectionHelper). + On("FindOne", context.Background(), bson.M{"error": false}). + Return(srHelperCorrect) + + dbHelper.(*mocks.DatabaseHelper). + On("Collection", "warrants").Return(collectionHelper) + + // Create new database with mocked Database interface + userDba := databases.NewWarrantDatabase(dbHelper) + + // Call method with defined filter, that in our mocked function returns + // mocked-error + user, err := userDba.FindOne(context.Background(), bson.M{"error": true}) + + assert.Empty(t, user) + assert.EqualError(t, err, "mocked-error") + + // Now call the same function with different filter for correct + // result + user, err = userDba.FindOne(context.Background(), bson.M{"error": false}) + + assert.Equal(t, &models.Warrant{ID: "mocked-user"}, user) + assert.NoError(t, err) +} + +func TestWarrantDatabase_Find(t *testing.T) { + + // define variables for interfaces + var dbHelper databases.DatabaseHelper + var collectionHelper databases.CollectionHelper + var srHelperErr databases.SingleResultHelper + var srHelperCorrect databases.SingleResultHelper + + // set interfaces implementation to mocked structures + dbHelper = &mocks.DatabaseHelper{} + collectionHelper = &mocks.CollectionHelper{} + srHelperErr = &mocks.SingleResultHelper{} + srHelperCorrect = &mocks.SingleResultHelper{} + + srHelperErr.(*mocks.SingleResultHelper). + On("Decode", mock.Anything). + Return(errors.New("mocked-error")) + + srHelperCorrect.(*mocks.SingleResultHelper). + On("Decode", mock.Anything). + Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Warrant) + *arg = []models.Warrant{{ID: "mocked-user"}} + }) + + collectionHelper.(*mocks.CollectionHelper). + On("Find", context.Background(), bson.M{"error": true}). + Return(srHelperErr) + + collectionHelper.(*mocks.CollectionHelper). + On("Find", context.Background(), bson.M{"error": false}). + Return(srHelperCorrect) + + dbHelper.(*mocks.DatabaseHelper). + On("Collection", "warrants").Return(collectionHelper) + + // Create new database with mocked Database interface + userDba := databases.NewWarrantDatabase(dbHelper) + + // Call method with defined filter, that in our mocked function returns + // mocked-error + user, err := userDba.Find(context.Background(), bson.M{"error": true}) + + assert.Empty(t, user) + assert.EqualError(t, err, "mocked-error") + + // Now call the same function with different filter for correct + // result + user, err = userDba.Find(context.Background(), bson.M{"error": false}) + + assert.Equal(t, []models.Warrant{{ID: "mocked-user"}}, user) + assert.NoError(t, err) +} diff --git a/docs/docs.go b/docs/docs.go index fa013c3..c66f181 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -472,3 +472,47 @@ type callByCommunityIDParamsWrapper struct { // in:query Status bool `json:"status"` } + +// swagger:route GET /api/v1/warrant/{warrant_id} warrant warrantByID +// Get a warrant by ID. +// responses: +// 200: warrantByIDResponse +// 404: errorMessageResponse + +// Shows a warrant by the given warrant ID {warrant_id} +// swagger:response warrantByIDResponse +type warrantByIDResponseWrapper struct { + // in:body + Body models.Warrant +} + +// swagger:route GET /api/v1/warrants warrant warrants +// Get all warrants. +// responses: +// 200: warrantsResponse +// 404: errorMessageResponse + +// Shows all warrants. +// swagger:response warrantsResponse +type warrantsResponseWrapper struct { + // in:body + Body []models.Warrant +} + +// swagger:route GET /api/v1/warrants/user/{user_id} warrant warrantsByUserID +// Get all warrants by userID. +// responses: +// 200: warrantsResponse + +// Shows all warrants by userID +// swagger:response warrantsResponse +type warrantsByUserIDResponseWrapper struct { + // in:body + Body []models.Warrant +} + +// swagger:parameters warrantsByUserID +type warrantsByUserIDParamsWrapper struct { + // in:query + Page string `json:"page"` +} diff --git a/models/warrant.go b/models/warrant.go new file mode 100644 index 0000000..c3c6688 --- /dev/null +++ b/models/warrant.go @@ -0,0 +1,22 @@ +package models + +// Warrant holds the structure for the warrant collection in mongo +type Warrant struct { + ID string `json:"_id" bson:"_id"` + Details WarrantDetails `json:"warrant" bson:"warrant"` + Version int32 `json:"__v" bson:"__v"` +} + +// WarrantDetails holds the structure for the inner user structure as +// defined in the warrant collection in mongo +type WarrantDetails struct { + Status bool `json:"status" bson:"status"` + AccusedID string `json:"accusedID" bson:"accusedID"` + AccusedFirstName string `json:"accusedFirstName" bson:"accusedFirstName"` + AccusedLastName string `json:"accusedLastName" bson:"accusedLastName"` + Reasons []string `json:"reasons" bson:"reasons"` + ReportingOfficerID string `json:"reportingOfficerID" bson:"reportingOfficerID"` + ClearingOfficerID string `json:"clearingOfficerID" bson:"clearingOfficerID"` + CreatedAt interface{} `json:"createdAt" bson:"createdAt"` + UpdatedAt interface{} `json:"updatedAt" bson:"updatedAt"` +} From 6c49352eb97bde8b84ddae1948a954f06cf15fcc Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Sun, 1 Oct 2023 09:45:47 -0700 Subject: [PATCH 12/15] update docs with search route --- docs/docs.go | 24 +++---- docs/swagger.yaml | 157 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 135 insertions(+), 46 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index c66f181..b954241 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -152,25 +152,25 @@ type civiliansByUserIDParamsWrapper struct { ActiveCommunityID string `json:"active_community_id"` } -// swagger:route GET /api/v1/name-search name-search nameSearchID -// Get a civilian by firstname, lastname, date-of-birth and communityID. +// swagger:route GET /api/v1/civilians/search civilian civiliansByNameSearch +// Search civilians by supplied params. // responses: -// 200: nameSearchResponse +// 200: civiliansResponse -// Shows a civilian by the given firstname, lastname, date-of-birth and communityID -// swagger:response nameSearchResponse -type nameSearchResponseWrapper struct { +// Shows all civilians by search params +// swagger:response civiliansResponse +type civiliansByNameSearchResponseWrapper struct { // in:body Body []models.Civilian } -// swagger:parameters nameSearchID -type nameSearchParamsWrapper struct { +// swagger:parameters civiliansByNameSearch +type civiliansByNameSearchParamsWrapper struct { // in:query - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - DateOfBirth string `json:"dob"` - CommunityID string `json:"community_id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + DateOfBirth string `json:"date_of_birth"` + ActiveCommunityID string `json:"active_community_id"` } // swagger:route GET /api/v1/vehicle/{vehicle_id} vehicle vehicleByID diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 61bc4a2..4af2012 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -575,6 +575,56 @@ definitions: x-go-name: Vin type: object x-go-package: github.com/linesmerrill/police-cad-api/models + Warrant: + description: Warrant holds the structure for the warrant collection in mongo + properties: + __v: + format: int32 + type: integer + x-go-name: Version + _id: + type: string + x-go-name: ID + warrant: + $ref: '#/definitions/WarrantDetails' + type: object + x-go-package: github.com/linesmerrill/police-cad-api/models + WarrantDetails: + description: |- + WarrantDetails holds the structure for the inner user structure as + defined in the warrant collection in mongo + properties: + accusedFirstName: + type: string + x-go-name: AccusedFirstName + accusedID: + type: string + x-go-name: AccusedID + accusedLastName: + type: string + x-go-name: AccusedLastName + clearingOfficerID: + type: string + x-go-name: ClearingOfficerID + createdAt: + type: object + x-go-name: CreatedAt + reasons: + items: + type: string + type: array + x-go-name: Reasons + reportingOfficerID: + type: string + x-go-name: ReportingOfficerID + status: + type: boolean + x-go-name: Status + updatedAt: + type: object + x-go-name: UpdatedAt + type: object + x-go-package: github.com/linesmerrill/police-cad-api/models host: https://police-cad-api.herokuapp.com info: description: Documentation of Lines Police CAD API. @@ -637,6 +687,32 @@ paths: summary: Get all civilians. tags: - civilian + /api/v1/civilians/search: + get: + operationId: civiliansByNameSearch + parameters: + - in: query + name: first_name + type: string + x-go-name: FirstName + - in: query + name: last_name + type: string + x-go-name: LastName + - in: query + name: date_of_birth + type: string + x-go-name: DateOfBirth + - in: query + name: active_community_id + type: string + x-go-name: ActiveCommunityID + responses: + "200": + $ref: '#/responses/civiliansResponse' + summary: Search civilians by supplied params. + tags: + - civilian /api/v1/civilians/user/{user_id}: get: operationId: civiliansByUserID @@ -846,32 +922,6 @@ paths: summary: Get all licenses by userID. tags: - license - /api/v1/name-search: - get: - operationId: nameSearchID - parameters: - - in: query - name: first_name - type: string - x-go-name: FirstName - - in: query - name: last_name - type: string - x-go-name: LastName - - in: query - name: dob - type: string - x-go-name: DateOfBirth - - in: query - name: community_id - type: string - x-go-name: CommunityID - responses: - "200": - $ref: '#/responses/nameSearchResponse' - summary: Get a civilian by firstname, lastname, date-of-birth and communityID. - tags: - - name-search /api/v1/user/{user_id}: get: operationId: userByID @@ -935,6 +985,42 @@ paths: summary: Get all vehicles by userID. tags: - vehicle + /api/v1/warrant/{warrant_id}: + get: + operationId: warrantByID + responses: + "200": + $ref: '#/responses/warrantByIDResponse' + "404": + $ref: '#/responses/errorMessageResponse' + summary: Get a warrant by ID. + tags: + - warrant + /api/v1/warrants: + get: + operationId: warrants + responses: + "200": + $ref: '#/responses/warrantsResponse' + "404": + $ref: '#/responses/errorMessageResponse' + summary: Get all warrants. + tags: + - warrant + /api/v1/warrants/user/{user_id}: + get: + operationId: warrantsByUserID + parameters: + - in: query + name: page + type: string + x-go-name: Page + responses: + "200": + $ref: '#/responses/warrantsResponse' + summary: Get all warrants by userID. + tags: + - warrant /health: get: operationId: healthEndpointID @@ -962,7 +1048,7 @@ responses: schema: $ref: '#/definitions/Civilian' civiliansResponse: - description: Shows all civilians by userID + description: Shows all civilians by search params schema: items: $ref: '#/definitions/Civilian' @@ -1031,13 +1117,6 @@ responses: items: $ref: '#/definitions/License' type: array - nameSearchResponse: - description: Shows a civilian by the given firstname, lastname, date-of-birth - and communityID - schema: - items: - $ref: '#/definitions/Civilian' - type: array userByIDResponse: description: Shows the user by the given userID {user_id} schema: @@ -1058,6 +1137,16 @@ responses: items: $ref: '#/definitions/Vehicle' type: array + warrantByIDResponse: + description: Shows a warrant by the given warrant ID {warrant_id} + schema: + $ref: '#/definitions/Warrant' + warrantsResponse: + description: Shows all warrants by userID + schema: + items: + $ref: '#/definitions/Warrant' + type: array schemes: - https securityDefinitions: From 787005aa9dc709e622e24c15e104362b68082ce1 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Sun, 1 Oct 2023 18:04:33 -0700 Subject: [PATCH 13/15] add status param to warrant route --- api/handlers/warrant.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api/handlers/warrant.go b/api/handlers/warrant.go index fbdda9c..ac99535 100644 --- a/api/handlers/warrant.go +++ b/api/handlers/warrant.go @@ -89,6 +89,7 @@ func (v Warrant) WarrantByIDHandler(w http.ResponseWriter, r *http.Request) { func (v Warrant) WarrantsByUserIDHandler(w http.ResponseWriter, r *http.Request) { userID := mux.Vars(r)["user_id"] activeCommunityID := r.URL.Query().Get("active_community_id") + status := r.URL.Query().Get("status") Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) if err != nil { zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) @@ -108,9 +109,15 @@ func (v Warrant) WarrantsByUserIDHandler(w http.ResponseWriter, r *http.Request) // // Likewise, if the user is not in a community, then we will display only the warrants // that are not in a community + statusBool := true + if status == "false" { + statusBool = false + } + err = nil dbResp, err = v.DB.Find(context.TODO(), bson.M{ "warrant.accusedID": userID, + "warrant.status": statusBool, }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) if err != nil { config.ErrorStatus("failed to get warrants", http.StatusNotFound, w, err) From fe1051dffab2747cc709a8c881396cbc8001cfcf Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Fri, 6 Oct 2023 16:12:13 -0700 Subject: [PATCH 14/15] add vehicle search by plate; add tests around new route; --- api/handlers/api.go | 1 + api/handlers/vehicle.go | 63 ++++++++++ api/handlers/vehicle_test.go | 236 +++++++++++++++++++++++++++++++++++ api/handlers/warrant_test.go | 48 +++++++ docs/docs.go | 19 +++ docs/swagger.yaml | 20 ++- 6 files changed, 386 insertions(+), 1 deletion(-) diff --git a/api/handlers/api.go b/api/handlers/api.go index 28a389f..509694f 100644 --- a/api/handlers/api.go +++ b/api/handlers/api.go @@ -56,6 +56,7 @@ func (a *App) New() *mux.Router { apiCreate.Handle("/vehicles", api.Middleware(http.HandlerFunc(v.VehicleHandler))).Methods("GET") apiCreate.Handle("/vehicles/user/{user_id}", api.Middleware(http.HandlerFunc(v.VehiclesByUserIDHandler))).Methods("GET") apiCreate.Handle("/vehicles/registered-owner/{registered_owner_id}", api.Middleware(http.HandlerFunc(v.VehiclesByRegisteredOwnerIDHandler))).Methods("GET") + apiCreate.Handle("/vehicles/search", api.Middleware(http.HandlerFunc(v.VehiclesByPlateSearchHandler))).Methods("GET") apiCreate.Handle("/firearm/{firearm_id}", api.Middleware(http.HandlerFunc(f.FirearmByIDHandler))).Methods("GET") apiCreate.Handle("/firearms", api.Middleware(http.HandlerFunc(f.FirearmHandler))).Methods("GET") apiCreate.Handle("/firearms/user/{user_id}", api.Middleware(http.HandlerFunc(f.FirearmsByUserIDHandler))).Methods("GET") diff --git a/api/handlers/vehicle.go b/api/handlers/vehicle.go index 09c6613..efa2a12 100644 --- a/api/handlers/vehicle.go +++ b/api/handlers/vehicle.go @@ -176,3 +176,66 @@ func (v Vehicle) VehiclesByRegisteredOwnerIDHandler(w http.ResponseWriter, r *ht w.WriteHeader(http.StatusOK) w.Write(b) } + +// VehiclesByPlateSearchHandler returns paginated list of vehicles that match the give plate +func (v Vehicle) VehiclesByPlateSearchHandler(w http.ResponseWriter, r *http.Request) { + plate := r.URL.Query().Get("plate") + activeCommunityID := r.URL.Query().Get("active_community_id") // optional + Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + zap.S().Warnf(fmt.Sprintf("limit not set, using default of %v", Limit|10)) + } + limit64 := int64(Limit) + Page = getPage(Page, r) + skip64 := int64(Page * Limit) + + zap.S().Debugf("plate: '%v'", plate) + zap.S().Debugf("active_community: '%v'", activeCommunityID) + + var dbResp []models.Vehicle + + // If the user is in a community then we want to search for vehicles that + // are in that same community. This way each user can have different vehicles + // across different communities. + // + // Likewise, if the user is not in a community, then we will display only the vehicles + // that are not in a community + err = nil + if activeCommunityID != "" && activeCommunityID != "null" && activeCommunityID != "undefined" { + dbResp, err = v.DB.Find(context.TODO(), bson.M{ + "$text": bson.M{ + "$search": fmt.Sprintf("%s", plate), + }, + "vehicle.activeCommunityID": activeCommunityID, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get vehicle plate search with active community id", http.StatusNotFound, w, err) + return + } + } else { + dbResp, err = v.DB.Find(context.TODO(), bson.M{ + "vehicle.plate": plate, + "$or": []bson.M{ + {"vehicle.activeCommunityID": nil}, + {"vehicle.activeCommunityID": ""}, + }, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get vehicle plate search with empty active community id", http.StatusNotFound, w, err) + return + } + } + + // Because the frontend requires that the data elements inside models.Vehicles exist, if + // len == 0 then we will just return an empty data object + if len(dbResp) == 0 { + dbResp = []models.Vehicle{} + } + b, err := json.Marshal(dbResp) + if err != nil { + config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err) + return + } + w.WriteHeader(http.StatusOK) + w.Write(b) +} diff --git a/api/handlers/vehicle_test.go b/api/handlers/vehicle_test.go index d01b277..17d49c7 100644 --- a/api/handlers/vehicle_test.go +++ b/api/handlers/vehicle_test.go @@ -925,3 +925,239 @@ func TestVehicle_VehiclesByRegisteredOwnerIDHandlerEmptyResponse(t *testing.T) { t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) } } + +func TestVehicle_VehiclesByPlateSearchHandlerJsonMarshalError(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/vehicles/search", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + x := map[string]interface{}{ + "foo": make(chan int), + } + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Vehicle) + *arg = []models.Vehicle{{Details: models.VehicleDetails{CreatedAt: x}}} + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) + + vehicleDatabase := databases.NewVehicleDatabase(db) + u := handlers.Vehicle{ + DB: vehicleDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.VehiclesByPlateSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to marshal response", Error: "json: unsupported type: chan int"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestVehicle_VehiclesByPlateSearchHandlerFailedToFindOne(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/vehicles/search?active_community_id=61c74b7b88e1abdac307bb39", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("failed to get vehicle plate search with empty active community id")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) + + vehicleDatabase := databases.NewVehicleDatabase(db) + u := handlers.Vehicle{ + DB: vehicleDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.VehiclesByPlateSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get vehicle plate search with active community id", Error: "failed to get vehicle plate search with empty active community id"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestVehicle_VehiclesByPlateSearchHandlerFailedToFindOneWithEmptyCommunityID(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/vehicles/search", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(errors.New("failed to get vehicle plate search with empty active community id")) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) + + vehicleDatabase := databases.NewVehicleDatabase(db) + u := handlers.Vehicle{ + DB: vehicleDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.VehiclesByPlateSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusNotFound { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) + } + + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get vehicle plate search with empty active community id", Error: "failed to get vehicle plate search with empty active community id"}} + b, _ := json.Marshal(expected) + if rr.Body.String() != string(b) { + t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) + } +} + +func TestVehicle_VehiclesByPlateSearchHandlerSuccess(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/vehicles/search?active_community_id=61be0ebf22cfea7e7550f00e", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Vehicle) + *arg = []models.Vehicle{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) + + vehicleDatabase := databases.NewVehicleDatabase(db) + u := handlers.Vehicle{ + DB: vehicleDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.VehiclesByPlateSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testVehicle []models.Vehicle + _ = json.Unmarshal(rr.Body.Bytes(), &testVehicle) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testVehicle[0].ID) +} + +func TestVehicle_VehiclesByPlateSearchHandlerEmptyResponse(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/vehicles/search", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var cursorHelper databases.CursorHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + cursorHelper = &mocks.CursorHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + cursorHelper.(*mocks.CursorHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Vehicle) + *arg = nil + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(cursorHelper) + db.(*MockDatabaseHelper).On("Collection", "vehicles").Return(conn) + + vehicleDatabase := databases.NewVehicleDatabase(db) + u := handlers.Vehicle{ + DB: vehicleDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.VehiclesByPlateSearchHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) + } + + expected := "[]" + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: \ngot: %v \nwant: %v", rr.Body.String(), expected) + } +} diff --git a/api/handlers/warrant_test.go b/api/handlers/warrant_test.go index e15f24f..73d25ce 100644 --- a/api/handlers/warrant_test.go +++ b/api/handlers/warrant_test.go @@ -592,6 +592,54 @@ func TestWarrant_WarrantsByUserIDHandlerSuccess(t *testing.T) { assert.Equal(t, "5fc51f36c72ff10004dca381", testWarrant[0].ID) } +func TestWarrant_WarrantsByUserIDHandlerSuccessWithStatusFalse(t *testing.T) { + req, err := http.NewRequest("GET", "/api/v1/warrants/user/61be0ebf22cfea7e7550f00e?active_community_id=61c74b7b88e1abdac307bb39&status=false", nil) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Authorization", "Bearer abc123") + + var db databases.DatabaseHelper + var client databases.ClientHelper + var conn databases.CollectionHelper + var singleResultHelper databases.SingleResultHelper + + db = &MockDatabaseHelper{} // can be used as db = &mocks.DatabaseHelper{} + client = &mocks.ClientHelper{} + conn = &mocks.CollectionHelper{} + singleResultHelper = &mocks.SingleResultHelper{} + + client.(*mocks.ClientHelper).On("StartSession").Return(nil, errors.New("mocked-error")) + db.(*MockDatabaseHelper).On("Client").Return(client) + singleResultHelper.(*mocks.SingleResultHelper).On("Decode", mock.Anything).Return(nil).Run(func(args mock.Arguments) { + arg := args.Get(0).(*[]models.Warrant) + *arg = []models.Warrant{{ID: "5fc51f36c72ff10004dca381"}} + + }) + conn.(*mocks.CollectionHelper).On("Find", mock.Anything, mock.Anything, mock.Anything).Return(singleResultHelper) + db.(*MockDatabaseHelper).On("Collection", "warrants").Return(conn) + + warrantDatabase := databases.NewWarrantDatabase(db) + u := handlers.Warrant{ + DB: warrantDatabase, + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(u.WarrantsByUserIDHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusBadRequest) + } + + var testWarrant []models.Warrant + _ = json.Unmarshal(rr.Body.Bytes(), &testWarrant) + + assert.Equal(t, "5fc51f36c72ff10004dca381", testWarrant[0].ID) +} + func TestWarrant_WarrantsByUserIDHandlerSuccessWithActiveCommunityID(t *testing.T) { req, err := http.NewRequest("GET", "/api/v1/warrants/user/61be0ebf22cfea7e7550f00e?active_community_id=61c74b7b88e1abdac307bb39", nil) if err != nil { diff --git a/docs/docs.go b/docs/docs.go index b954241..e1deb2c 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -229,6 +229,25 @@ type vehiclesByRegisteredOwnerIDResponseWrapper struct { Body []models.Vehicle } +// swagger:route GET /api/v1/vehicles/search vehicle vehiclesByPlateSearch +// Get all vehicles by plate. +// responses: +// 200: vehiclesResponse + +// Shows all vehicles by plate +// swagger:response vehiclesResponse +type vehiclesByPlateSearchResponseWrapper struct { + // in:body + Body []models.Vehicle +} + +// swagger:parameters vehiclesByPlateSearch +type vehiclesByPlateSearchParamsWrapper struct { + // in:query + Plate string `json:"plate"` + ActiveCommunityID string `json:"active_community_id"` +} + // swagger:route GET /api/v1/firearm/{firearm_id} firearm firearmByID // Get a firearm by ID. // responses: diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 4af2012..0f76ab8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -971,6 +971,24 @@ paths: summary: Get all vehicles by RegisteredOwnerID. tags: - vehicle + /api/v1/vehicles/search: + get: + operationId: vehiclesByPlateSearch + parameters: + - in: query + name: plate + type: string + x-go-name: Plate + - in: query + name: active_community_id + type: string + x-go-name: ActiveCommunityID + responses: + "200": + $ref: '#/responses/vehiclesResponse' + summary: Get all vehicles by plate. + tags: + - vehicle /api/v1/vehicles/user/{user_id}: get: operationId: vehiclesByUserID @@ -1132,7 +1150,7 @@ responses: schema: $ref: '#/definitions/Vehicle' vehiclesResponse: - description: Shows all vehicles by RegisteredOwnerID + description: Shows all vehicles by plate schema: items: $ref: '#/definitions/Vehicle' From 174f202c642b77b5d59e6289c8243d9b37ffc953 Mon Sep 17 00:00:00 2001 From: Merrill Lines Date: Sun, 8 Oct 2023 07:41:49 -0700 Subject: [PATCH 15/15] remove date-of-brith from name search with non communities --- api/handlers/civilian.go | 37 ++++++++++------------------------- api/handlers/civilian_test.go | 8 ++++---- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/api/handlers/civilian.go b/api/handlers/civilian.go index fa04afb..ad65860 100644 --- a/api/handlers/civilian.go +++ b/api/handlers/civilian.go @@ -148,7 +148,6 @@ func (c Civilian) CiviliansByUserIDHandler(w http.ResponseWriter, r *http.Reques func (c Civilian) CiviliansByNameSearchHandler(w http.ResponseWriter, r *http.Request) { firstName := r.URL.Query().Get("first_name") lastName := r.URL.Query().Get("last_name") - dateOfBirth := r.URL.Query().Get("date_of_birth") // optional activeCommunityID := r.URL.Query().Get("active_community_id") // optional Limit, err := strconv.Atoi(r.URL.Query().Get("limit")) if err != nil { @@ -158,7 +157,7 @@ func (c Civilian) CiviliansByNameSearchHandler(w http.ResponseWriter, r *http.Re Page = getPage(Page, r) skip64 := int64(Page * Limit) - zap.S().Debugf("first_name: '%v', last_name: '%v', date_of_birth: '%v'", firstName, lastName, dateOfBirth) + zap.S().Debugf("first_name: '%v', last_name: '%v'", firstName, lastName) zap.S().Debugf("active_community: '%v'", activeCommunityID) var dbResp []models.Civilian @@ -170,31 +169,15 @@ func (c Civilian) CiviliansByNameSearchHandler(w http.ResponseWriter, r *http.Re // Likewise, if the user is not in a community, then we will display only the civilians // that are not in a community err = nil - if activeCommunityID != "" && activeCommunityID != "null" && activeCommunityID != "undefined" { - dbResp, err = c.DB.Find(context.TODO(), bson.M{ - "$text": bson.M{ - "$search": fmt.Sprintf("%s %s", firstName, lastName), - }, - "civilian.activeCommunityID": activeCommunityID, - }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) - if err != nil { - config.ErrorStatus("failed to get civilian name search with active community id", http.StatusNotFound, w, err) - return - } - } else { - dbResp, err = c.DB.Find(context.TODO(), bson.M{ - "civilian.firstName": firstName, - "civilian.lastName": lastName, - "civilian.birthday": dateOfBirth, - "$or": []bson.M{ - {"civilian.activeCommunityID": nil}, - {"civilian.activeCommunityID": ""}, - }, - }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) - if err != nil { - config.ErrorStatus("failed to get civilian name search with empty active community id", http.StatusNotFound, w, err) - return - } + dbResp, err = c.DB.Find(context.TODO(), bson.M{ + "$text": bson.M{ + "$search": fmt.Sprintf("%s %s", firstName, lastName), + }, + "civilian.activeCommunityID": activeCommunityID, + }, &options.FindOptions{Limit: &limit64, Skip: &skip64}) + if err != nil { + config.ErrorStatus("failed to get civilian name search", http.StatusNotFound, w, err) + return } // Because the frontend requires that the data elements inside models.Civilians exist, if diff --git a/api/handlers/civilian_test.go b/api/handlers/civilian_test.go index c5863cb..0ac4be7 100644 --- a/api/handlers/civilian_test.go +++ b/api/handlers/civilian_test.go @@ -968,7 +968,7 @@ func TestCivilian_CiviliansByNameSearchHandlerFailedToFindOne(t *testing.T) { t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) } - expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get civilian name search with active community id", Error: "mongo: no documents in result"}} + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get civilian name search", Error: "mongo: no documents in result"}} b, _ := json.Marshal(expected) if rr.Body.String() != string(b) { t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) @@ -1013,7 +1013,7 @@ func TestCivilian_CiviliansByNameSearchHandlerActiveCommunityIDFailedToFindOne(t t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) } - expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get civilian name search with active community id", Error: "mongo: no documents in result"}} + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get civilian name search", Error: "mongo: no documents in result"}} b, _ := json.Marshal(expected) if rr.Body.String() != string(b) { t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) @@ -1165,7 +1165,7 @@ func TestCivilian_CiviliansByNameSearchHandlerSuccessWithNullCommunityID(t *test } func TestCivilian_CiviliansByNameSearchHandlerFailedToFindOneWithEmptyCommunityID(t *testing.T) { - req, err := http.NewRequest("GET", "/api/v1/civilians/search?active_community_id=null&first_name=alessandro&last_name=mills&date_of_birth=1987-03-20", nil) + req, err := http.NewRequest("GET", "/api/v1/civilians/search?active_community_id=null&first_name=alessandro&last_name=mills", nil) if err != nil { t.Fatal(err) } @@ -1202,7 +1202,7 @@ func TestCivilian_CiviliansByNameSearchHandlerFailedToFindOneWithEmptyCommunityI t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound) } - expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get civilian name search with empty active community id", Error: "mongo: no documents in result"}} + expected := models.ErrorMessageResponse{Response: models.MessageError{Message: "failed to get civilian name search", Error: "mongo: no documents in result"}} b, _ := json.Marshal(expected) if rr.Body.String() != string(b) { t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)