211 lines
5.2 KiB
Go
211 lines
5.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/fiskerinc/cloud-services/pkg/cache"
|
|
re "github.com/fiskerinc/cloud-services/pkg/redis"
|
|
"github.com/fiskerinc/cloud-services/pkg/utils"
|
|
"github.com/gomodule/redigo/redis"
|
|
"github.com/pkg/errors"
|
|
|
|
"otaupdate/services"
|
|
|
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
|
orm "github.com/fiskerinc/cloud-services/pkg/db/queries"
|
|
"github.com/fiskerinc/cloud-services/pkg/logger"
|
|
"github.com/fiskerinc/cloud-services/pkg/loggerdataresp"
|
|
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
|
|
"github.com/fiskerinc/cloud-services/pkg/utils/urlhelper"
|
|
"github.com/fiskerinc/cloud-services/pkg/validator"
|
|
"github.com/fiskerinc/cloud-services/pkg/utils/elptr"
|
|
)
|
|
|
|
// HandleVehiclesGet godoc
|
|
// @Summary Search cars
|
|
// @Description Returns cars filtered by id, model, year, and vin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param Authorization header string false "Bearer <ID token>"
|
|
// @Param Api-Key header string false "<API token>"
|
|
// @Param search query string false "Text search"
|
|
// @Param vins query []string false "csv of Car vin"
|
|
// @Param model query int false "Car model"
|
|
// @Param year query string false "Car year"
|
|
// @Param online query bool false "Car status"
|
|
// @Param online_hmi query bool false "HMI status"
|
|
// @Param limit query int false "Max number of records"
|
|
// @Param offset query int false "Records offset"
|
|
// @Param order query string false "Sort on column with asc or desc"
|
|
// @Success 200 {object} common.JSONDBQueryResult{data=[]common.Car}
|
|
// @Failure 400 {object} common.JSONError "Bad request"
|
|
// @Failure 401 {object} common.JSONError "Unauthorized"
|
|
// @Failure 503 {object} common.JSONError "Service unavailable"
|
|
// @Router /vehicles [get]
|
|
func HandleVehiclesGet(w http.ResponseWriter, r *http.Request) {
|
|
var total int
|
|
c := services.GetDB().GetCars()
|
|
filter, err := parseCarsFilter(r)
|
|
if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) {
|
|
return
|
|
}
|
|
|
|
options, err := orm.ParsePageQuery(r)
|
|
if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) {
|
|
return
|
|
}
|
|
if options.Order == "" {
|
|
options.Order = "vin"
|
|
}
|
|
|
|
err = fillOnline(filter)
|
|
if loggerdataresp.BadDataErrorResp(w, err, http.StatusServiceUnavailable) {
|
|
return
|
|
}
|
|
|
|
// Do not display EU vins in the list in NA prod UI
|
|
if isNAProd() {
|
|
filter.NoEU = true
|
|
}
|
|
|
|
cars, err := c.Search(filter, options)
|
|
if loggerdataresp.BadDataErrorResp(w, err, http.StatusServiceUnavailable, loggerdataresp.PostgresNoRowsErrorCheck) {
|
|
return
|
|
}
|
|
|
|
if options.Offset == 0 && filter.VIN == "" {
|
|
total, err = c.SearchCount(filter)
|
|
if loggerdataresp.BadDataErrorResp(w, err, http.StatusServiceUnavailable) {
|
|
return
|
|
}
|
|
}
|
|
|
|
utils.RespJSON(w, http.StatusOK, common.JSONDBQueryResult{
|
|
Data: cars,
|
|
Total: total,
|
|
})
|
|
}
|
|
|
|
var vehiclesCache cache.VehicleCacher
|
|
|
|
func SetVehiclesCache(c cache.VehicleCacher) {
|
|
vehiclesCache = c
|
|
}
|
|
|
|
func GetVehiclesCache() cache.VehicleCacher {
|
|
if vehiclesCache == nil {
|
|
var err error
|
|
vehiclesCache, err = cache.NewVehiclesCache(
|
|
envtool.GetEnvDuration("VEHICLES_CACHE_TTL", 5*time.Minute),
|
|
envtool.GetEnvInt("VEHICLES_CACHE_LIMIT", 1000))
|
|
if err != nil {
|
|
logger.Error().Err(err).Send()
|
|
}
|
|
}
|
|
|
|
return vehiclesCache
|
|
}
|
|
|
|
func fillOnline(filter *common.CarSearch) error {
|
|
if filter == nil || filter.Online == nil {
|
|
return nil
|
|
}
|
|
|
|
var keys []string
|
|
if filter.Online.Online != nil && *filter.Online.Online {
|
|
keys = append(keys, re.CarSessionsKey())
|
|
}
|
|
|
|
if filter.Online.HMI != nil && *filter.Online.HMI {
|
|
keys = append(keys, re.HMISessionsKey())
|
|
}
|
|
|
|
if len(keys) == 0 {
|
|
return nil
|
|
}
|
|
|
|
vehicles, err := getOnline(keys)
|
|
if err != nil {
|
|
return errors.WithMessagef(err, "get online vehicles")
|
|
}
|
|
|
|
filter.Online.VINsOnline = vehicles
|
|
|
|
return nil
|
|
}
|
|
|
|
func getOnline(sessionKeys []string) ([]string, error) {
|
|
var onlineVehicles []string
|
|
redisCli := services.RedisClientPool().GetFromPool()
|
|
defer redisCli.Close()
|
|
|
|
batch := re.NewRedisBatchCommands()
|
|
|
|
for i := range sessionKeys {
|
|
batch.Add("SMEMBERS", sessionKeys[i])
|
|
}
|
|
|
|
vinOfVins, err := redis.Values(redisCli.ExecuteBatch(batch))
|
|
if err != nil {
|
|
return nil, errors.WithMessagef(err, "redis get multi failed for keys: %v", sessionKeys)
|
|
}
|
|
|
|
onlineVehicles = make([]string, 0)
|
|
|
|
for _, rvins := range vinOfVins {
|
|
setVehicles, err := redis.Strings(rvins, nil)
|
|
if err != nil {
|
|
return nil, errors.WithMessagef(err, "redis cast to string failed: %v", sessionKeys)
|
|
}
|
|
|
|
onlineVehicles = append(onlineVehicles, setVehicles...)
|
|
}
|
|
|
|
return onlineVehicles, nil
|
|
}
|
|
|
|
func parseCarsFilter(r *http.Request) (*common.CarSearch, error) {
|
|
qs := r.URL.Query()
|
|
|
|
filter := common.CarSearch{
|
|
Search: qs.Get("search"),
|
|
VINs: qs.Get("vins"),
|
|
Car: common.Car{
|
|
VIN: qs.Get("vin"),
|
|
Model: qs.Get("model"),
|
|
Year: urlhelper.GetQueryInt(qs, "year"),
|
|
},
|
|
}
|
|
|
|
var o *common.CarOnlineFilter
|
|
|
|
online, ok := urlhelper.GetQueryBool(qs, "online")
|
|
if ok {
|
|
if o == nil {
|
|
o = &common.CarOnlineFilter{}
|
|
}
|
|
|
|
o.Online = elptr.ElPtr(online)
|
|
}
|
|
|
|
onlineHMI, ok := urlhelper.GetQueryBool(qs, "online_hmi")
|
|
if ok {
|
|
if o == nil {
|
|
o = &common.CarOnlineFilter{}
|
|
}
|
|
|
|
o.HMI = elptr.ElPtr(onlineHMI)
|
|
}
|
|
|
|
filter.Online = o
|
|
|
|
err := validator.ValidateNonRequired(filter)
|
|
|
|
return &filter, err
|
|
}
|
|
|
|
func isNAProd() bool {
|
|
return envtool.GetEnv("IS_NA_PROD", "") != ""
|
|
}
|