Dirty implementation of createVotes
This commit is contained in:
@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -140,22 +141,95 @@ func (app *application) createVotes(w http.ResponseWriter, r *http.Request) {
|
||||
app.serverError(w, r, err)
|
||||
}
|
||||
|
||||
// TODO check if he has voted
|
||||
|
||||
//var voterIdentity string
|
||||
if election.AreVotersKnown {
|
||||
if request.VoterIdentity == nil || validator.Blank(*request.VoterIdentity) {
|
||||
app.unprocessableEntityErrorSingle(w, fmt.Errorf("election has known voters; you must provide an identity provided by the organizer"))
|
||||
for _, c := range request.Choices {
|
||||
choiceExists := slices.Contains(election.Choices, c.ChoiceText)
|
||||
if !choiceExists {
|
||||
app.unprocessableEntityErrorSingle(w, fmt.Errorf("choice %v doesn't exist", c.ChoiceText))
|
||||
return
|
||||
}
|
||||
//voterIdentity = *request.VoterIdentity
|
||||
} else {
|
||||
// TODO: get requester's IP address as identity
|
||||
}
|
||||
|
||||
// TODO verify if choice exists
|
||||
// TODO count tokens to make sure user isn't trying to cheat
|
||||
tokensUsed := 0
|
||||
for _, c := range request.Choices {
|
||||
tokensUsed += c.Tokens
|
||||
}
|
||||
|
||||
json, _ := json.Marshal(election)
|
||||
w.Write(json)
|
||||
if tokensUsed > election.Tokens {
|
||||
app.unprocessableEntityErrorSingle(w, fmt.Errorf("you used too many tokens; must not exceed %v tokens", election.Tokens))
|
||||
return
|
||||
}
|
||||
|
||||
electionHasExpired := election.ExpiresAt.Before(time.Now())
|
||||
if electionHasExpired {
|
||||
app.unprocessableEntityErrorSingle(w, fmt.Errorf("election has expired"))
|
||||
return
|
||||
}
|
||||
|
||||
// this snippet of code also inserts in the `voters` table
|
||||
voterIdentity := func() string {
|
||||
var voterIdentity string
|
||||
if election.AreVotersKnown {
|
||||
if request.VoterIdentity == nil || validator.Blank(*request.VoterIdentity) {
|
||||
app.unprocessableEntityErrorSingle(w, fmt.Errorf("election has known voters; you must provide an identity provided by the organizer"))
|
||||
return ""
|
||||
}
|
||||
|
||||
voterIdentity = *request.VoterIdentity
|
||||
hasCastVotes, err := app.votes.Exists(voterIdentity, election.ID)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return ""
|
||||
}
|
||||
if hasCastVotes {
|
||||
app.unprocessableEntityErrorSingle(w, fmt.Errorf("you already voted"))
|
||||
return ""
|
||||
}
|
||||
} else {
|
||||
voterIdentity = r.RemoteAddr
|
||||
|
||||
// if voters are known, voter will always exist
|
||||
voterExists, err := app.voters.Exists(voterIdentity, election.ID)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return ""
|
||||
}
|
||||
if voterExists {
|
||||
app.unprocessableEntityErrorSingle(w, fmt.Errorf("you already voted"))
|
||||
return ""
|
||||
}
|
||||
|
||||
_, err = app.voters.Insert(voterIdentity, election.ID)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return voterIdentity
|
||||
}()
|
||||
|
||||
if voterIdentity == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if !election.AreVotersKnown {
|
||||
voterCount, err := app.voters.CountByElection(election.ID)
|
||||
if err != nil && !errors.Is(sql.ErrNoRows, err) {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
// if voters are known, voterCount == election.MaxVoters in all cases
|
||||
if voterCount == election.MaxVoters {
|
||||
app.unprocessableEntityErrorSingle(w, fmt.Errorf("maximum voters reached"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range request.Choices {
|
||||
_, err := app.votes.Insert(voterIdentity, election.ID, c.ChoiceText, c.Tokens)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ func main() {
|
||||
logger: logger,
|
||||
elections: &models.ElectionModel{DB: db},
|
||||
voters: &models.VoterModel{DB: db},
|
||||
votes: &models.VoteModel{DB: db},
|
||||
}
|
||||
|
||||
logger.Info("Starting server", "addr", addr)
|
||||
|
@ -11,6 +11,7 @@ type application struct {
|
||||
logger *slog.Logger
|
||||
elections models.ElectionModelInterface
|
||||
voters models.VoterModelInterface
|
||||
votes models.VoteModelInterface
|
||||
}
|
||||
|
||||
func (app *application) routes() http.Handler {
|
||||
|
@ -15,6 +15,7 @@ func newTestApplication(t *testing.T) *application {
|
||||
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
||||
elections: &mockElectionModel{},
|
||||
voters: &mockVoterModel{},
|
||||
votes: &mockVoteModel{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,8 +57,8 @@ func (e *mockElectionModel) GetById(id int) (*models.Election, error) {
|
||||
Tokens: 100,
|
||||
AreVotersKnown: false,
|
||||
MaxVoters: 10,
|
||||
CreatedAt: time.Now().String(),
|
||||
ExpiresAt: time.Now().Add(100 * time.Hour).String(),
|
||||
CreatedAt: time.Now(),
|
||||
ExpiresAt: time.Now().Add(100 * time.Hour),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -67,3 +68,22 @@ type mockVoterModel struct {
|
||||
func (v *mockVoterModel) Insert(identity string, electionID int) (int, error) {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (v *mockVoterModel) CountByElection(electionID int) (int, error) {
|
||||
return 10, nil
|
||||
}
|
||||
|
||||
func (v *mockVoterModel) Exists(voterIdentity string, electionID int) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type mockVoteModel struct {
|
||||
}
|
||||
|
||||
func (v *mockVoteModel) Insert(voterIdentity string, electionId int, choiceText string, tokens int) (int, error) {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (v *mockVoteModel) Exists(voterIdentity string, electionID int) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user