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 " // @Param Api-Key header string false "" // @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", "") != "" }