func TestReadUserDirection(t *testing.T) { readDirection := new(data.UserDirection) testRead(t, "/users/"+user.Username+"/direction", readDirection) readDirection.UpdateTime = direction.UpdateTime // hack structJSONCompare(t, direction, readDirection) }
func scanUserDirection(direction *data.UserDirection, rows *sql.Rows) error { var updateTimeString string err := rows.Scan( &direction.Username, &direction.LockUsername, &direction.Command, &direction.Heading, &direction.Lat, &direction.Lng, &updateTimeString, ) if err != nil { return err } updateTime, err := time.Parse("2006-01-02 15:04:05", updateTimeString) // this assumes UTC as timezone if err != nil { log.Println("UserDirection scanner failed to parse time.") return err } direction.UpdateTime = updateTime return nil }
func TestSelectUserDirection(t *testing.T) { selectedDirection := new(data.UserDirection) selectedDirection.Username = user.Username err := Select(selectedDirection) if err != nil { t.Error(err) } t.Log(selectedDirection.UpdateTime.UTC()) t.Log(direction.UpdateTime.UTC()) selectedDirection.UpdateTime = direction.UpdateTime // hack structJSONCompare(t, direction, selectedDirection) }
// Generic Read handler for reading single objects func Read(w http.ResponseWriter, r *http.Request) { //No Auth for the moment w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") vars := mux.Vars(r) var ( val interface{} // Generic container for the read object err error ) // Build a URI like representation of the datatype types := []string{vars["datatype"]} if childtype, ok := vars["childtype"]; ok { types = append(types, childtype) } // Switch based on that URI like representation and instantiate something in the generic container. Also infer the identifier from the vars and perform validation. switch strings.Join(types, "/") { case "items": item := new(data.Item) item.ID, err = strconv.ParseInt(vars["key"], 10, 64) val = item case "items/comments": comment := new(data.ItemComment) comment.ID, err = strconv.ParseInt(vars["childkey"], 10, 64) val = comment case "users": user := new(data.User) user.Username = vars["key"] val = user case "users/direction": direction := new(data.UserDirection) direction.Username = vars["key"] val = direction case "roles": role := new(data.Role) role.Title = vars["key"] val = role case "taxonomy": term := new(data.Term) term.Term = vars["key"] val = term case "taxonomy/ranking": ranking := new(data.TermRanking) ranking.Term = vars["key"] val = ranking default: http.NotFound(w, r) return } if err != nil { log.Println(err) http.Error(w, "Malformed key in URI", http.StatusBadRequest) return } // Perform the Select err = storage.Select(val) if err == storage.ErrZeroAffected { http.Error(w, "No matches for query", http.StatusNotFound) return } else if err != nil { log.Println(err) http.Error(w, "Select Database error, likely due to malformed request", http.StatusInternalServerError) return } // Important. Let's never send salt/hash out. switch v := val.(type) { case *data.User: v.Salt = "" v.Password = "" } w.Header().Set("Content-Type", "application/json") // Header are important when GZIP is enabled // Return the object we've selected from the database. encoder := json.NewEncoder(w) err = encoder.Encode(val) if err != nil { log.Println(err) } }
func Insert(val interface{}) error { var ( res sql.Result err error ) now := time.Now().UTC() switch v := val.(type) { case *data.Item: res, err = Exec( "INSERT INTO Items (Type, Author, ThumbnailURI, ContentURI, UploadURI, ContentToken, HasHeading, Heading, HasGeo, Lat, Lng, Radius, Live, StartTime, StopTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", v.Type, v.Author, v.ThumbnailURI, v.ContentURI, v.UploadURI, v.ContentToken, v.HasHeading, v.Heading, v.HasGeo, v.Lat, v.Lng, v.Radius, v.Live, v.StartTime.UTC(), v.StopTime.UTC(), ) case *data.ItemComment: res, err = Exec( "INSERT INTO ItemComments (ItemID, Author, Body, UpdateTime) VALUES (?, ?, ?, ?)", v.ItemID, v.Author, v.Body, now, ) case *data.Term: // There is basically no danger with INSERT IGNORE there is nothing we would want to change if there is // accidental remake of a term res, err = Exec( "INSERT IGNORE INTO Terms (Term, Automated, Author, UpdateTime) VALUES (?, ?, ?, ?)", v.Term, v.Automated, v.Author, now, ) case *data.TermRelationship: // Nothing can go wrong with INSERT IGNORE since the key is whole entry res, err = Exec( "INSERT IGNORE INTO TermRelationships (Term, ItemID) VALUES (?, ?)", v.Term, v.ItemID, ) case *data.TermRanking: // There is basically no danger with INSERT IGNORE there is nothing we would want to change if there is // accidental remake of a term res, err = Exec( "INSERT IGNORE INTO TermRankings (Term, Ranking, UpdateTime) VALUES (?, ?, ?)", v.Term, v.Ranking, now, ) case *data.Role: res, err = Exec( "INSERT INTO Roles (Title, Permissions) VALUES (?, ?)", v.Title, v.Permissions, ) case *data.User: res, err = Exec( "INSERT INTO Users (Username, Password, Salt, Role, TrustLevel, CreateTime) VALUES (?, ?, ?, ?, ?, ?)", v.Username, v.Password, v.Salt, v.Role, v.TrustLevel, now, ) case *data.UserDirection: res, err = Exec( "INSERT INTO UserDirections (Username, LockUsername, Command, Heading, Lat, Lng, UpdateTime) VALUES (?, ?, ?, ?, ?, ?, ?)", v.Username, v.LockUsername, v.Command, v.Heading, v.Lat, v.Lng, now, ) default: return ErrUnsupportedDataType } if err != nil { return err } affected, err := res.RowsAffected() if err != nil { return err } if affected < 1 { return ErrZeroAffected } ID, err := res.LastInsertId() if err != nil { return err } switch v := val.(type) { case *data.Item: v.ID = ID v.AddTerm("all", v.Author) v.AddTerm("type:"+v.Type, v.Author) v.AddTerm(v.Author, v.Author) _, err = ReconcileTerms(v, &v.Terms) case *data.ItemComment: v.ID = ID v.UpdateTime = now case *data.Term: v.UpdateTime = now ranking := new(data.TermRanking) ranking.Term = v.Term err = Insert(ranking) case *data.TermRanking: v.UpdateTime = now case *data.User: v.CreateTime = now direction := new(data.UserDirection) direction.Username = v.Username err = Insert(direction) case *data.UserDirection: v.UpdateTime = now } listeners.NotifyInsert(val) return err }
// Handle a POST request with an image, phone_id, lat, lng and heading. Return a target heading from the web UI func MultiUploadHandler(rterDir string, uploadPath string, w http.ResponseWriter, r *http.Request) { imageFile, header, err := r.FormFile("image") if err != nil { return } user := new(data.User) user.Username = r.FormValue("phone_id") err = storage.Select(user) if err == storage.ErrZeroAffected { user.Role = "public" storage.Insert(user) // log.Println("upload failed, phone_id invalid:", user.Username) // http.Error(w, "Invalid credentials.", http.StatusUnauthorized) // return } else if err != nil { log.Println(err) http.Error(w, "Database error, likely due to malformed request.", http.StatusInternalServerError) return } os.Mkdir(filepath.Join(uploadPath, user.Username), os.ModeDir|0775) matchingItems := make([]*data.Item, 0) err = storage.SelectWhere(&matchingItems, "WHERE Type=\"streaming-video-v0\" AND Author=?", user.Username) exists := true if err == storage.ErrZeroAffected { exists = false } else if err != nil { log.Println(err) http.Error(w, "Database error, likely due to malformed request.", http.StatusInternalServerError) return } var item *data.Item if exists { item = matchingItems[0] } else { item = new(data.Item) } item.Author = user.Username item.Type = "streaming-video-v0" item.Live = true item.HasGeo = true item.HasHeading = true item.Lat, err = strconv.ParseFloat(r.FormValue("lat"), 64) if err != nil { item.HasGeo = false } item.Lng, err = strconv.ParseFloat(r.FormValue("lng"), 64) if err != nil { item.HasGeo = false } item.Heading, err = strconv.ParseFloat(r.FormValue("heading"), 64) if err != nil { item.HasHeading = false } item.StartTime = time.Now() item.StopTime = item.StartTime path := uploadPath if strings.HasSuffix(header.Filename, ".png") { path = filepath.Join(path, fmt.Sprintf("%v/%v.png", user.Username, item.StopTime.UnixNano())) } else if strings.HasSuffix(header.Filename, ".jpg") || strings.HasSuffix(header.Filename, "jpeg") { path = filepath.Join(path, fmt.Sprintf("%v/%v.jpg", user.Username, item.StopTime.UnixNano())) } outputFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0664) if err != nil { log.Println(err) http.Error(w, "Error, likely due to malformed request.", http.StatusInternalServerError) } defer outputFile.Close() io.Copy(outputFile, imageFile) path = path[len(rterDir):] item.ContentURI = path item.ThumbnailURI = path if exists { storage.Update(item) } else { storage.Insert(item) } userDirection := new(data.UserDirection) userDirection.Username = user.Username err = storage.Select(userDirection) if err != nil { log.Println(err) http.Error(w, "Database error, likely due to malformed request.", http.StatusInternalServerError) return } w.Write([]byte(strconv.FormatFloat(userDirection.Heading, 'f', 6, 64))) }