func TestReadTermRanking(t *testing.T) { readRanking := new(data.TermRanking) testRead(t, "/taxonomy/"+term.Term+"/ranking", readRanking) readRanking.UpdateTime = ranking.UpdateTime structJSONCompare(t, ranking, readRanking) }
func TestSelectTermRanking(t *testing.T) { selectedRanking := new(data.TermRanking) selectedRanking.Term = ranking.Term err := Select(selectedRanking) if err != nil { t.Error(err) } t.Log(ranking.UpdateTime.UTC()) t.Log(selectedRanking.UpdateTime.UTC()) selectedRanking.UpdateTime = ranking.UpdateTime structJSONCompare(t, ranking, selectedRanking) }
func scanTermRanking(ranking *data.TermRanking, rows *sql.Rows) error { var updateTimeString string err := rows.Scan( &ranking.Term, &ranking.Ranking, &updateTimeString, ) if err != nil { return err } // TODO: this is a hacky fix for null times if updateTimeString == "0000-00-00 00:00:00" { updateTimeString = "0001-01-01 00:00:00" } updateTime, err := time.Parse("2006-01-02 15:04:05", updateTimeString) // this assumes UTC as timezone if err != nil { log.Println("TermRanking scanner failed to parse time.") return err } ranking.UpdateTime = updateTime return nil }
// 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 }