package handlers import ( "bytes" "encoding/json" "net/http" "otaupdate/utils" "strconv" "time" "github.com/fiskerinc/cloud-services/pkg/common" u "github.com/fiskerinc/cloud-services/pkg/utils" "github.com/julienschmidt/httprouter" "github.com/fiskerinc/cloud-services/pkg/loggerdataresp" ) var ReadAzureBlobFun = utils.ReadAzureBlob // HandleTrexLogsGet godoc // @Summary Retrieve T.Rex logs // @Description Get T.Rex logs for specific day // @Accept json // @Produce json // @Param Authorization header string false "Bearer " // @Param Api-Key header string false "" // @Param vin path string true "Car vin" // @Param date query string true "Logs date" // @Param offset query int false "Offset in bytes" // @Param count query int false "Count in bytes" // @Param direction query string false "Cursor direction 'up' or 'down', default is 'down'" // @Success 200 {object} common.JSONBlobReadResult{data=[]common.LogTrexLog} // @Failure 400 {object} common.JSONError "Bad request" // @Failure 401 {object} common.JSONError "Unauthorized" // @Failure 404 {object} common.JSONError "Status not found" // @Failure 503 {object} common.JSONError "Service unavailable" // @Router /vehicle/{vin}/trex-logs [get] func HandleTrexLogsGet(w http.ResponseWriter, r *http.Request) { vin, date, direction, offset, count, ok := parse(w, r) if !ok { return } buf, blobSize, err := ReadAzureBlobFun(utils.AzureTRexLogsContainerName, vin, date.Year(), int(date.Month()), date.Day(), offset, count, direction) if err != nil { u.RespError(w, http.StatusNotFound, err.Error()) return } res, bytesRead, resOffset := processBlob(buf, direction, offset) if len(res) == 0 { resOffset = offset bytesRead = 0 } u.RespJSON(w, http.StatusOK, common.JSONBlobReadResult{ Data: res, RealOffset: resOffset, BytesRead: int64(bytesRead), BlobSize: blobSize, }) } func processBlob(buf []byte, direction utils.Direction, offset int64) (res []json.RawMessage, bytesRead int64, resOffset int64) { bytesRead = int64(len(buf)) splitted := bytes.Split(buf, []byte("\n")) res = make([]json.RawMessage, 0, len(splitted)) var parsedLog common.LogTrexLog resOffset = offset for i, row := range splitted { if len(row) == 0 { continue } //do not add first/last row, if it contains incomplete JSON if i == 0 && json.Unmarshal(row, &parsedLog) != nil { if direction == utils.Down { resOffset += int64(len(row)) } bytesRead -= int64(len(row)) continue } if i == len(splitted)-1 && json.Unmarshal(row, &parsedLog) != nil { if direction == utils.Up { resOffset += int64(len(row)) } bytesRead -= int64(len(row)) continue } res = append(res, json.RawMessage(row)) } return res, bytesRead, resOffset } func parse(w http.ResponseWriter, r *http.Request) (vin string, date time.Time, direction utils.Direction, offset, count int64, ok bool) { values := r.URL.Query() params := httprouter.ParamsFromContext(r.Context()) vin = params.ByName("vin") dateStr := values.Get("date") offset, err := strconv.ParseInt(values.Get("offset"), 10, 64) if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) { return } count, err = strconv.ParseInt(values.Get("count"), 10, 64) if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) { return } date, err = time.Parse("2006-01-02", dateStr) if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) { return } direction, ok = utils.ParseCursorDirection(values.Get("direction")) if !ok { direction = utils.Down ok = true } return vin, date, direction, offset, count, ok }