Move voting to a new endpoint
/election/{id}/votes
This commit is contained in:
@ -11,6 +11,7 @@ import (
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -111,8 +112,6 @@ type createVotesRequestWithValidator struct {
|
||||
}
|
||||
|
||||
func (r *createVotesRequestWithValidator) isValid() bool {
|
||||
r.CheckField(validator.GreaterThan(r.ElectionId, 0), "electionId", "must be greater than 0")
|
||||
|
||||
for _, choice := range r.Choices {
|
||||
r.CheckField(validator.NotBlank(choice.ChoiceText), "choiceText", "must not be blank")
|
||||
}
|
||||
@ -133,10 +132,16 @@ func (app *application) createVotes(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
election, err := app.elections.GetById(request.ElectionId) // get from path instead of from the JSON
|
||||
electionID, err := strconv.Atoi(r.PathValue("id"))
|
||||
if err != nil {
|
||||
app.clientError(w, http.StatusBadRequest, "Couldn't convert the id you provided to a number")
|
||||
return
|
||||
}
|
||||
|
||||
election, err := app.elections.GetById(electionID)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
app.unprocessableEntityErrorSingle(w, fmt.Errorf("election with id %v doesn't exist", request.ElectionId)) // TODO: return 404
|
||||
app.clientError(w, http.StatusNotFound, "Couldn't find an election with the ID you provided")
|
||||
return
|
||||
}
|
||||
app.serverError(w, r, err)
|
||||
@ -145,7 +150,7 @@ func (app *application) createVotes(w http.ResponseWriter, r *http.Request) {
|
||||
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))
|
||||
app.clientError(w, http.StatusUnprocessableEntity, fmt.Sprintf("choice %v doesn't exist", c.ChoiceText))
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -156,13 +161,13 @@ func (app *application) createVotes(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if tokensUsed > election.Tokens {
|
||||
app.unprocessableEntityErrorSingle(w, fmt.Errorf("you used too many tokens; must not exceed %v tokens", election.Tokens))
|
||||
app.clientError(w, http.StatusUnprocessableEntity, fmt.Sprintf("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"))
|
||||
app.clientError(w, http.StatusUnprocessableEntity, "election has expired")
|
||||
return
|
||||
}
|
||||
|
||||
@ -193,9 +198,9 @@ func (app *application) createVotes(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (app *application) createVotesHandleKnownVotersElection(w http.ResponseWriter, r *http.Request, request createVotesRequestWithValidator, election *models.Election) (string, error) {
|
||||
if request.VoterIdentity == nil || validator.Blank(*request.VoterIdentity) {
|
||||
err := fmt.Errorf("election has known voters; you must provide an identity provided by the organizer")
|
||||
app.unprocessableEntityErrorSingle(w, err)
|
||||
return "", err
|
||||
message := "election has known voters; you must provide an identity provided by the organizer"
|
||||
app.clientError(w, http.StatusUnprocessableEntity, message)
|
||||
return "", fmt.Errorf(message)
|
||||
}
|
||||
|
||||
voterIdentity := *request.VoterIdentity
|
||||
@ -205,9 +210,9 @@ func (app *application) createVotesHandleKnownVotersElection(w http.ResponseWrit
|
||||
return "", err
|
||||
}
|
||||
if hasCastVotes {
|
||||
err := fmt.Errorf("you already voted")
|
||||
app.unprocessableEntityErrorSingle(w, err)
|
||||
return "", err
|
||||
message := "you already voted"
|
||||
app.clientError(w, http.StatusUnprocessableEntity, message)
|
||||
return "", fmt.Errorf(message)
|
||||
}
|
||||
|
||||
voterExists, err := app.voters.Exists(voterIdentity, election.ID)
|
||||
@ -216,9 +221,9 @@ func (app *application) createVotesHandleKnownVotersElection(w http.ResponseWrit
|
||||
return "", err
|
||||
}
|
||||
if !voterExists {
|
||||
err := fmt.Errorf("invalid voter identity")
|
||||
app.unprocessableEntityErrorSingle(w, err)
|
||||
return "", err
|
||||
message := "invalid voter identity"
|
||||
app.clientError(w, http.StatusUnprocessableEntity, message)
|
||||
return "", fmt.Errorf(message)
|
||||
}
|
||||
|
||||
return voterIdentity, nil
|
||||
@ -233,9 +238,9 @@ func (app *application) createVotesHandleUnknownVotersElection(w http.ResponseWr
|
||||
return "", err
|
||||
}
|
||||
if voterExists {
|
||||
err := fmt.Errorf("you already voted")
|
||||
app.unprocessableEntityErrorSingle(w, err)
|
||||
return "", err
|
||||
message := "you already voted"
|
||||
app.clientError(w, http.StatusUnprocessableEntity, message)
|
||||
return "", fmt.Errorf(message)
|
||||
}
|
||||
|
||||
_, err = app.voters.Insert(voterIdentity, election.ID)
|
||||
@ -250,9 +255,9 @@ func (app *application) createVotesHandleUnknownVotersElection(w http.ResponseWr
|
||||
return "", err
|
||||
}
|
||||
if voterCount == election.MaxVoters {
|
||||
err := fmt.Errorf("maximum voters reached")
|
||||
app.unprocessableEntityErrorSingle(w, err)
|
||||
return "", err
|
||||
message := "maximum voters reached"
|
||||
app.clientError(w, http.StatusUnprocessableEntity, message)
|
||||
return "", fmt.Errorf(message)
|
||||
}
|
||||
|
||||
return voterIdentity, nil
|
||||
|
@ -254,7 +254,7 @@ func TestCreateVotes_UnknownVotersElection(t *testing.T) {
|
||||
On("Insert", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(1, nil)
|
||||
|
||||
path := "/votes"
|
||||
path := "/election/1/votes"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -273,7 +273,6 @@ func TestCreateVotes_UnknownVotersElection(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 40},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: nil,
|
||||
},
|
||||
expectedCode: http.StatusCreated,
|
||||
@ -289,7 +288,6 @@ func TestCreateVotes_UnknownVotersElection(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 41},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: nil,
|
||||
},
|
||||
expectedCode: http.StatusUnprocessableEntity,
|
||||
@ -305,7 +303,6 @@ func TestCreateVotes_UnknownVotersElection(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddh", Tokens: 40},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: nil,
|
||||
},
|
||||
expectedCode: http.StatusUnprocessableEntity,
|
||||
@ -362,7 +359,7 @@ func TestCreateVotes_KnownVotersElection(t *testing.T) {
|
||||
On("Exists", mock.Anything, mock.Anything).
|
||||
Return(false, nil)
|
||||
|
||||
path := "/votes"
|
||||
path := "/election/1/votes"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -381,7 +378,6 @@ func TestCreateVotes_KnownVotersElection(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 40},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: &EXISTING_VOTER_IDENTITY,
|
||||
},
|
||||
expectedCode: http.StatusCreated,
|
||||
@ -397,7 +393,6 @@ func TestCreateVotes_KnownVotersElection(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 40},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: &NON_EXISTING_VOTER_IDENTITY,
|
||||
},
|
||||
expectedCode: http.StatusUnprocessableEntity,
|
||||
@ -413,7 +408,6 @@ func TestCreateVotes_KnownVotersElection(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 40},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: nil,
|
||||
},
|
||||
expectedCode: http.StatusUnprocessableEntity,
|
||||
@ -429,7 +423,6 @@ func TestCreateVotes_KnownVotersElection(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 41},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: &EXISTING_VOTER_IDENTITY,
|
||||
},
|
||||
expectedCode: http.StatusUnprocessableEntity,
|
||||
@ -445,7 +438,6 @@ func TestCreateVotes_KnownVotersElection(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddh", Tokens: 40},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: &EXISTING_VOTER_IDENTITY,
|
||||
},
|
||||
expectedCode: http.StatusUnprocessableEntity,
|
||||
@ -475,7 +467,7 @@ func TestCreateVotes_NonExistingElection(t *testing.T) {
|
||||
On("GetById", mock.Anything).
|
||||
Return((*models.Election)(nil), sql.ErrNoRows)
|
||||
|
||||
path := "/votes"
|
||||
path := "/election/1/votes"
|
||||
requestBody := api.CreateVotesRequest{
|
||||
Choices: []struct {
|
||||
ChoiceText string `json:"choiceText"`
|
||||
@ -484,7 +476,6 @@ func TestCreateVotes_NonExistingElection(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 40},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: nil,
|
||||
}
|
||||
requestBodyJson, err := json.Marshal(requestBody)
|
||||
@ -494,7 +485,38 @@ func TestCreateVotes_NonExistingElection(t *testing.T) {
|
||||
|
||||
code, _, _ := server.post(t, path, bytes.NewReader(requestBodyJson))
|
||||
|
||||
assert.Equal(t, http.StatusUnprocessableEntity, code)
|
||||
assert.Equal(t, http.StatusNotFound, code)
|
||||
}
|
||||
|
||||
func TestCreateVotes_NonNumberElectionID(t *testing.T) {
|
||||
app := newTestApplication(t)
|
||||
server := newTestServer(t, app.routes())
|
||||
defer server.Close()
|
||||
|
||||
mockElections := app.elections.(*mockElectionModel)
|
||||
mockElections.
|
||||
On("GetById", mock.Anything).
|
||||
Return((*models.Election)(nil), sql.ErrNoRows)
|
||||
|
||||
path := "/election/1a/votes"
|
||||
requestBody := api.CreateVotesRequest{
|
||||
Choices: []struct {
|
||||
ChoiceText string `json:"choiceText"`
|
||||
Tokens int `json:"tokens"`
|
||||
}{
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 40},
|
||||
},
|
||||
VoterIdentity: nil,
|
||||
}
|
||||
requestBodyJson, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
code, _, _ := server.post(t, path, bytes.NewReader(requestBodyJson))
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
|
||||
func TestCreateVotes_AlreadyVoted(t *testing.T) {
|
||||
@ -533,7 +555,8 @@ func TestCreateVotes_AlreadyVoted(t *testing.T) {
|
||||
On("Exists", mock.Anything, mock.Anything).
|
||||
Return(true, nil)
|
||||
|
||||
path := "/votes"
|
||||
existingElectionPath := "/election/1/votes"
|
||||
nonExistingElectionPath := "/election/1/votes"
|
||||
voterIdentity := "anything"
|
||||
|
||||
tests := []struct {
|
||||
@ -544,7 +567,7 @@ func TestCreateVotes_AlreadyVoted(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "Invalid request for known voters election (already voted)",
|
||||
urlPath: path,
|
||||
urlPath: existingElectionPath,
|
||||
body: api.CreateVotesRequest{
|
||||
Choices: []struct {
|
||||
ChoiceText string `json:"choiceText"`
|
||||
@ -553,14 +576,13 @@ func TestCreateVotes_AlreadyVoted(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 40},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: &voterIdentity,
|
||||
},
|
||||
expectedCode: http.StatusUnprocessableEntity,
|
||||
},
|
||||
{
|
||||
name: "Invalid request for unknown voters election (already voted)",
|
||||
urlPath: path,
|
||||
urlPath: nonExistingElectionPath,
|
||||
body: api.CreateVotesRequest{
|
||||
Choices: []struct {
|
||||
ChoiceText string `json:"choiceText"`
|
||||
@ -569,7 +591,6 @@ func TestCreateVotes_AlreadyVoted(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 40},
|
||||
},
|
||||
ElectionId: 2,
|
||||
VoterIdentity: &voterIdentity,
|
||||
},
|
||||
expectedCode: http.StatusUnprocessableEntity,
|
||||
@ -619,7 +640,7 @@ func TestCreateVotes_UnknownVotersElectionMaxVotersReached(t *testing.T) {
|
||||
On("CountByElection", mock.Anything).
|
||||
Return(10, nil)
|
||||
|
||||
path := "/votes"
|
||||
path := "/election/1/votes"
|
||||
requestBody := api.CreateVotesRequest{
|
||||
Choices: []struct {
|
||||
ChoiceText string `json:"choiceText"`
|
||||
@ -628,7 +649,6 @@ func TestCreateVotes_UnknownVotersElectionMaxVotersReached(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 40},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: nil,
|
||||
}
|
||||
requestBodyJson, err := json.Marshal(requestBody)
|
||||
@ -671,7 +691,7 @@ func TestCreateVotes_ExpiredElection(t *testing.T) {
|
||||
On("CountByElection", mock.Anything).
|
||||
Return(10, nil)
|
||||
|
||||
path := "/votes"
|
||||
path := "/election/1/votes"
|
||||
requestBody := api.CreateVotesRequest{
|
||||
Choices: []struct {
|
||||
ChoiceText string `json:"choiceText"`
|
||||
@ -680,7 +700,6 @@ func TestCreateVotes_ExpiredElection(t *testing.T) {
|
||||
{ChoiceText: "Gandhi", Tokens: 60},
|
||||
{ChoiceText: "Buddha", Tokens: 40},
|
||||
},
|
||||
ElectionId: 1,
|
||||
VoterIdentity: nil,
|
||||
}
|
||||
requestBodyJson, err := json.Marshal(requestBody)
|
||||
|
@ -22,11 +22,9 @@ func (app *application) serverError(w http.ResponseWriter, r *http.Request, err
|
||||
func (app *application) clientError(w http.ResponseWriter, status int, message string) {
|
||||
w.WriteHeader(status)
|
||||
var response = api.ErrorResponse{
|
||||
Code: http.StatusUnprocessableEntity,
|
||||
Details: &map[string]interface{}{
|
||||
"error": message,
|
||||
},
|
||||
Message: "There was an error in the request",
|
||||
Code: status,
|
||||
Details: nil,
|
||||
Message: message,
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
@ -38,16 +36,7 @@ func (app *application) unprocessableEntityError(w http.ResponseWriter, v valida
|
||||
Details: &map[string]interface{}{
|
||||
"fields": v.FieldErrors,
|
||||
},
|
||||
Message: "Election data is invalid",
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func (app *application) unprocessableEntityErrorSingle(w http.ResponseWriter, err error) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
var response = api.ErrorResponse{
|
||||
Code: http.StatusUnprocessableEntity,
|
||||
Message: err.Error(),
|
||||
Message: "Request data is invalid",
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
@ -58,6 +58,46 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
/election/{id}/votes:
|
||||
post:
|
||||
tags:
|
||||
- vote
|
||||
summary: Cast your votes for an election
|
||||
operationId: createVotes
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The ID of the election
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateVotesRequest"
|
||||
responses:
|
||||
200:
|
||||
description: Votes cast
|
||||
404:
|
||||
description: Election not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
422:
|
||||
description: Unprocessable content
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
500:
|
||||
description: Server error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
/election/{id}/results:
|
||||
get:
|
||||
tags:
|
||||
@ -90,33 +130,6 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
/votes:
|
||||
post:
|
||||
tags:
|
||||
- vote
|
||||
summary: Cast your votes for an election
|
||||
operationId: createVotes
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateVotesRequest"
|
||||
responses:
|
||||
200:
|
||||
description: Votes cast
|
||||
422:
|
||||
description: Unprocessable content
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
500:
|
||||
description: Server error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
components:
|
||||
schemas:
|
||||
Election:
|
||||
@ -266,14 +279,11 @@ components:
|
||||
CreateVotesRequest:
|
||||
type: object
|
||||
required:
|
||||
- electionId
|
||||
- choices
|
||||
properties:
|
||||
voterIdentity:
|
||||
type: string
|
||||
description: Must be filled if election has known voters
|
||||
electionId:
|
||||
type: integer
|
||||
choices:
|
||||
type: array
|
||||
items:
|
||||
|
@ -18,7 +18,7 @@ func (app *application) routes() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("POST /election", app.createElection)
|
||||
mux.HandleFunc("POST /votes", app.createVotes)
|
||||
mux.HandleFunc("POST /election/{id}/votes", app.createVotes)
|
||||
|
||||
standard := alice.New(app.recoverPanic, app.logRequest)
|
||||
return standard.Then(mux)
|
||||
|
8
go.mod
8
go.mod
@ -7,15 +7,18 @@ require (
|
||||
github.com/golang-migrate/migrate/v4 v4.18.1
|
||||
github.com/justinas/alice v1.2.0
|
||||
github.com/mattn/go-sqlite3 v1.14.24
|
||||
github.com/oapi-codegen/runtime v1.1.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Eun/go-convert v0.0.0-20200421145326-bef6c56666ee // indirect
|
||||
github.com/Eun/go-doppelgangerreader v0.0.0-20190911075941-30f1527f16b2 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gookit/color v1.4.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
@ -24,8 +27,8 @@ require (
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/k0kubun/pp v3.0.1+incompatible // indirect
|
||||
github.com/lunixbochs/vtclean v1.0.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
@ -35,5 +38,6 @@ require (
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
20
go.sum
20
go.sum
@ -7,11 +7,15 @@ github.com/Eun/go-hit v0.5.23/go.mod h1:LCHZ6WSPFDXlTQkFUSLe0VsrOhzzEEzbPzCGc6FY
|
||||
github.com/Eun/go-testdoc v0.0.1/go.mod h1:uT+GeDi7TpqQx6MBkcfXD9nF15Q8IX+kTNEnUUPbuUo=
|
||||
github.com/Eun/yaegi-template v1.5.16/go.mod h1:eyFQ1QHbKLNHKpUvdjt8+99ZR1ji7lVVbduSK1M5N/U=
|
||||
github.com/Eun/yaegi-template v1.5.18/go.mod h1:iVHjge496SWL7hLf1euBZIO40Bk0R38g6lu8iyvpc30=
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/aaw/maybe_tls v0.0.0-20160803104303-89c499bcc6aa h1:6yJyU8MlPBB2enGJdPciPlr8P+PC0nhCFHnSHYMirZI=
|
||||
github.com/aaw/maybe_tls v0.0.0-20160803104303-89c499bcc6aa/go.mod h1:I0wzMZvViQzmJjxK+AtfFAnqDCkQV/+r17PO1CCSYnU=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
|
||||
github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1 h1:TEBmxO80TM04L8IuMWk77SGL1HomBmKTdzdJLLWznxI=
|
||||
github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@ -22,6 +26,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
|
||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
@ -39,6 +45,7 @@ github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921i
|
||||
github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
|
||||
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
|
||||
@ -54,12 +61,14 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
@ -70,9 +79,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
|
||||
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
@ -117,6 +129,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@ -136,8 +149,9 @@ golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSm
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -9,6 +9,8 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/oapi-codegen/runtime"
|
||||
)
|
||||
|
||||
// CreateElectionRequest defines model for CreateElectionRequest.
|
||||
@ -34,12 +36,14 @@ type CreateVotesRequest struct {
|
||||
ChoiceText string `json:"choiceText"`
|
||||
Tokens int `json:"tokens"`
|
||||
} `json:"choices"`
|
||||
ElectionId int `json:"electionId"`
|
||||
|
||||
// VoterIdentity Must be filled if election has known voters
|
||||
VoterIdentity *string `json:"voterIdentity,omitempty"`
|
||||
}
|
||||
|
||||
// ElectionResultsResponse defines model for ElectionResultsResponse.
|
||||
type ElectionResultsResponse = map[string]interface{}
|
||||
|
||||
// ErrorResponse defines model for ErrorResponse.
|
||||
type ErrorResponse struct {
|
||||
// Code Machine-readable error code
|
||||
@ -63,9 +67,12 @@ type ServerInterface interface {
|
||||
// Create a new election
|
||||
// (POST /election)
|
||||
CreateElection(w http.ResponseWriter, r *http.Request)
|
||||
// Get the results of an election
|
||||
// (GET /election/{id}/results)
|
||||
GetElectionIdResults(w http.ResponseWriter, r *http.Request, id int)
|
||||
// Cast your votes for an election
|
||||
// (POST /votes)
|
||||
CreateVotes(w http.ResponseWriter, r *http.Request)
|
||||
// (POST /election/{id}/votes)
|
||||
CreateVotes(w http.ResponseWriter, r *http.Request, id int)
|
||||
}
|
||||
|
||||
// ServerInterfaceWrapper converts contexts to parameters.
|
||||
@ -92,12 +99,49 @@ func (siw *ServerInterfaceWrapper) CreateElection(w http.ResponseWriter, r *http
|
||||
handler.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
// GetElectionIdResults operation middleware
|
||||
func (siw *ServerInterfaceWrapper) GetElectionIdResults(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "id" -------------
|
||||
var id int
|
||||
|
||||
err = runtime.BindStyledParameterWithOptions("simple", "id", r.PathValue("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
|
||||
if err != nil {
|
||||
siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
|
||||
return
|
||||
}
|
||||
|
||||
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
siw.Handler.GetElectionIdResults(w, r, id)
|
||||
}))
|
||||
|
||||
for _, middleware := range siw.HandlerMiddlewares {
|
||||
handler = middleware(handler)
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
// CreateVotes operation middleware
|
||||
func (siw *ServerInterfaceWrapper) CreateVotes(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "id" -------------
|
||||
var id int
|
||||
|
||||
err = runtime.BindStyledParameterWithOptions("simple", "id", r.PathValue("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
|
||||
if err != nil {
|
||||
siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
|
||||
return
|
||||
}
|
||||
|
||||
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
siw.Handler.CreateVotes(w, r)
|
||||
siw.Handler.CreateVotes(w, r, id)
|
||||
}))
|
||||
|
||||
for _, middleware := range siw.HandlerMiddlewares {
|
||||
@ -222,7 +266,8 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
|
||||
}
|
||||
|
||||
m.HandleFunc("POST "+options.BaseURL+"/election", wrapper.CreateElection)
|
||||
m.HandleFunc("POST "+options.BaseURL+"/votes", wrapper.CreateVotes)
|
||||
m.HandleFunc("GET "+options.BaseURL+"/election/{id}/results", wrapper.GetElectionIdResults)
|
||||
m.HandleFunc("POST "+options.BaseURL+"/election/{id}/votes", wrapper.CreateVotes)
|
||||
|
||||
return m
|
||||
}
|
||||
|
Reference in New Issue
Block a user