diff --git a/internal/models/candidate.go b/internal/models/candidate.go new file mode 100644 index 0000000000000000000000000000000000000000..22a6dcf1ad181f2bce0a825725df7f18a704c9c9 --- /dev/null +++ b/internal/models/candidate.go @@ -0,0 +1,125 @@ +package models + +import ( + "encoding/json" + "net/http" + "strconv" + "strings" + + "forge.grandlyon.com/apoyen/elections/internal/auth" +) + +func (d *DataHandler) handleCandidate(w http.ResponseWriter, r *http.Request) { + id, _ := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/api/Candidate/")) + switch method := r.Method; method { + case "GET": + switch auth.GetLoggedUserTechnical(w, r).Role { + case "ADMIN", "CAPTURER", "VISUALIZER": + d.getCandidate(w, r, id) + default: + http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError) + } + case "POST": + switch auth.GetLoggedUserTechnical(w, r).Role { + case "ADMIN": + d.postCandidate(w, r) + case "CAPTURER", "VISUALIZER": + http.Error(w, ErrorNotAuthorizeMethodOnRessource, http.StatusMethodNotAllowed) + default: + http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError) + } + + case "PUT": + switch auth.GetLoggedUserTechnical(w, r).Role { + case "ADMIN": + d.putCandidate(w, r, id) + case "CAPTURER", "VISUALIZER": + http.Error(w, ErrorNotAuthorizeMethodOnRessource, http.StatusMethodNotAllowed) + default: + http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError) + } + case "DELETE": + switch auth.GetLoggedUserTechnical(w, r).Role { + case "ADMIN": + d.deleteCandidate(w, r, id) + case "CAPTURER", "VISUALIZER": + http.Error(w, ErrorNotAuthorizeMethodOnRessource, http.StatusMethodNotAllowed) + default: + http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError) + } + default: + http.Error(w, "method not allowed", 400) + } +} + +func (d *DataHandler) getCandidate(w http.ResponseWriter, r *http.Request, id int) { + if id != 0 { + var o Candidate + if err := d.db.First(&o, id).Error; err != nil { + http.Error(w, ErrorIDIsMissing, http.StatusNotFound) + return + } + json.NewEncoder(w).Encode(o) + } else { + var o []Candidate + d.db.Find(&o) + json.NewEncoder(w).Encode(o) + } +} + +func (d *DataHandler) postCandidate(w http.ResponseWriter, r *http.Request) { + var o Candidate + err := json.NewDecoder(r.Body).Decode(&o) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Check that CandidateList exist + var candidateList CandidateList + if err := d.db.First(&candidateList, o.CandidateListID).Error; err != nil { + http.Error(w, ErrorParentNotFound, http.StatusInternalServerError) + return + } + + d.db.Create(&o) + d.db.Last(&o) + json.NewEncoder(w).Encode(o) +} + +func (d *DataHandler) putCandidate(w http.ResponseWriter, r *http.Request, id int) { + var o Candidate + if err := d.db.First(&o, id).Error; err != nil { + http.Error(w, ErrorIDIsMissing, http.StatusNotFound) + return + } + var candidate Candidate + err := json.NewDecoder(r.Body).Decode(&candidate) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + o.FullName = candidate.FullName + o.Rank = candidate.Rank + o.CommunityCounseller = candidate.CommunityCounseller + o.Birthdate = candidate.Birthdate + o.PotentialIncompatibility = candidate.PotentialIncompatibility + o.Refused = candidate.Refused + o.Removed = candidate.Removed + d.db.Save(&o) + json.NewEncoder(w).Encode(o) + +} + +func (d *DataHandler) deleteCandidate(w http.ResponseWriter, r *http.Request, id int) { + if id != 0 { + var o Candidate + if err := d.db.First(&o, id).Error; err != nil { + http.Error(w, ErrorIDIsMissing, http.StatusNotFound) + return + } + + d.db.Delete(&o) + } else { + http.Error(w, ErrorIDIsMissing, http.StatusNotFound) + } +} diff --git a/internal/models/models.go b/internal/models/models.go index 526d8c80af755583adce5bb2e881bf0a9aea0436..b5fb2d6f4403ec70c9325a0207d3eaba8f24dfdf 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -171,13 +171,13 @@ type Candidate struct { FullName string Rank uint CommunityCounseller bool - Birthdate time.Time + Birthdate string PotentialIncompatibility bool Refused bool Removed bool } -// Vote represent the number of voice between a CanidateList and a Desk (+blank and null) +// Vote represent the number of voice between a CandidateList and a Desk (+blank and null) type Vote struct { ID uint `gorm:"primary_key"` CreatedAt time.Time `json:"-"` @@ -238,6 +238,8 @@ func (d *DataHandler) ProcessAPI(w http.ResponseWriter, r *http.Request) { d.handleParty(w, r) case "CandidateList": d.handleCandidateList(w, r) + case "Candidate": + d.handleCandidate(w, r) } } diff --git a/internal/rootmux/admin_test.go b/internal/rootmux/admin_test.go index 29365cf0a36e66356ca2154b3d0d357ca052adce..5478c7efdc1ac7c3a5ca2e04cf44382e94c6051b 100644 --- a/internal/rootmux/admin_test.go +++ b/internal/rootmux/admin_test.go @@ -110,8 +110,19 @@ func AdminTests(t *testing.T) { // Update a CandidateList do("PUT", "/api/CandidateList/1", xsrfHeader, `{"ID":1,"Name":"MyBigList","PartyID":1,"RoundID":1,"AreaID":1}`, 200, `{"ID":1,"Name":"MyBigList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[],"Votes":[]}`) + // Create a Candidate + do("POST", "/api/Candidate", xsrfHeader, `{"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`, 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`) + // Get a Candidate + do("GET", "/api/Candidate/1", xsrfHeader, ``, 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`) + // Get Candidates + do("GET", "/api/Candidate/", xsrfHeader, ``, 200, `[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`) + // Update a Candidate + do("PUT", "/api/Candidate/1", xsrfHeader, `{"ID":1,"CandidateListID":1,"FullName":"CandidateTest","Rank":2,"CommunityCounseller":false,"Birthdate":"2020-02-28","PotentialIncompatibility":true,"Refused":false,"Removed":false}`, 200, `{"ID":1,"CandidateListID":1,"FullName":"CandidateTest","Rank":2,"CommunityCounseller":false,"Birthdate":"2020-02-28","PotentialIncompatibility":true,"Refused":false,"Removed":false}`) + // TODO Update a DeskRound to Validated=true can only be done when votes are captured + // Delete a Candidate + do("DELETE", "/api/Candidate/1", xsrfHeader, ``, 200, ``) // Delete a CandidateList do("DELETE", "/api/CandidateList/1", xsrfHeader, ``, 200, ``) // Delete a Party diff --git a/internal/rootmux/capturer_test.go b/internal/rootmux/capturer_test.go index 59c0b48661b70b06ef05fd44fa32dee4097ed9eb..6f6cc1b9bd21491fc5168e0d303c252d82661e05 100644 --- a/internal/rootmux/capturer_test.go +++ b/internal/rootmux/capturer_test.go @@ -115,14 +115,25 @@ func CapturerTests(t *testing.T) { // Create a CandidateList should fail with 405 do("POST", "/api/CandidateList", xsrfHeader, `{"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1}`, 405, `You're not authorize to execute this method on this ressource.`) // Get a candidateList - do("GET", "/api/CandidateList/1", xsrfHeader, "", 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[],"Votes":[]}`) + do("GET", "/api/CandidateList/1", xsrfHeader, "", 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}],"Votes":[]}`) // Get all the parties - do("GET", "/api/CandidateList/", xsrfHeader, "", 200, `[{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[],"Votes":[]}]`) + do("GET", "/api/CandidateList/", xsrfHeader, "", 200, `[{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}],"Votes":[]}]`) // Update a CandidateList should fail with 405 do("PUT", "/api/CandidateList/1", xsrfHeader, `{"ID":1,"Name":"MyBigList","PartyID":1,"RoundID":1,"AreaID":1}`, 405, `You're not authorize to execute this method on this ressource.`) // Delete a CandidateList should fail with 405 do("DELETE", "/api/CandidateList/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) + // Create a Candidate should fail with 405 + do("POST", "/api/Candidate", xsrfHeader, `{"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`, 405, `You're not authorize to execute this method on this ressource.`) + // Get a Candidate + do("GET", "/api/Candidate/1", xsrfHeader, "", 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`) + // Get all the parties + do("GET", "/api/Candidate/", xsrfHeader, "", 200, `[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}]`) + // Update a Candidate should fail with 405 + do("PUT", "/api/Candidate/1", xsrfHeader, `{"ID":1,"CandidateListID":1,"FullName":"CandidateTest","Rank":2,"CommunityCounseller":false,"Birthdate":"2020-02-28","PotentialIncompatibility":true,"Refused":false,"Removed":false}`, 405, `You're not authorize to execute this method on this ressource.`) + // Delete a Candidate should fail with 405 + do("DELETE", "/api/Candidate/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) + // Add deskround to capturer should fail with 405 do("POST", "/api/CapturerDeskRound", xsrfHeader, `{"CapturerID":1,"DeskRoundID":1}`, 405, `You're not authorize to execute this method on this ressource.`) // Remove DeskRound to capturer diff --git a/internal/rootmux/rootmux_test.go b/internal/rootmux/rootmux_test.go index 8221960964f4b4236d4f7351ec81d26454162ecf..f9ab37b26e8feee291b6d6de7d836f4234121ba3 100644 --- a/internal/rootmux/rootmux_test.go +++ b/internal/rootmux/rootmux_test.go @@ -222,6 +222,7 @@ func resetDataWithData(t *testing.T) { do("POST", "/api/Round", xsrfHeader, `{"ElectionID":1,"Date":"2020-06-28","Round":1}`, 200, `{"ID":1,"ElectionID":1,"Parameter":{"ID":0,"CountBlankAndNull":false,"ShowOnlyCompleted":false,"ShowMap":false},"Date":"2020-06-28","Round":1,"DeskRounds":null,"CandidateLists":null}`) do("POST", "/api/Party", xsrfHeader, `{"Name":"MyGreatParty","Color":"#FFFFFF"}`, 200, `{"ID":1,"Name":"MyGreatParty","Color":"#FFFFFF","CandidateLists":null}`) do("POST", "/api/CandidateList", xsrfHeader, `{"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1}`, 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":null,"Votes":null}`) + do("POST", "/api/Candidate", xsrfHeader, `{"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`, 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`) } do("POST", "/Login", noH, `{"login": "admin","password": "password"}`, 200, "") init() diff --git a/internal/rootmux/visualizer_test.go b/internal/rootmux/visualizer_test.go index 56eeee6a2ac159b233621d2aa70eb7c06302120b..6976b1401f82d16ce72183480490e83147211a11 100644 --- a/internal/rootmux/visualizer_test.go +++ b/internal/rootmux/visualizer_test.go @@ -102,14 +102,25 @@ func VisualizerTests(t *testing.T) { // Create a CandidateList should fail with 405 do("POST", "/api/CandidateList", xsrfHeader, `{"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1}`, 405, `You're not authorize to execute this method on this ressource.`) // Get a candidateList - do("GET", "/api/CandidateList/1", xsrfHeader, "", 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[],"Votes":[]}`) + do("GET", "/api/CandidateList/1", xsrfHeader, "", 200, `{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}],"Votes":[]}`) // Get all the parties - do("GET", "/api/CandidateList/", xsrfHeader, "", 200, `[{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[],"Votes":[]}]`) + do("GET", "/api/CandidateList/", xsrfHeader, "", 200, `[{"ID":1,"Name":"MyGreatList","PartyID":1,"RoundID":1,"AreaID":1,"Candidates":[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}],"Votes":[]}]`) // Update a CandidateList should fail with 405 do("PUT", "/api/CandidateList/1", xsrfHeader, `{"ID":1,"Name":"MyBigList","PartyID":1,"RoundID":1,"AreaID":1}`, 405, `You're not authorize to execute this method on this ressource.`) // Delete a CandidateList should fail with 405 do("DELETE", "/api/CandidateList/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) + // Create a Candidate should fail with 405 + do("POST", "/api/Candidate", xsrfHeader, `{"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`, 405, `You're not authorize to execute this method on this ressource.`) + // Get a Candidate + do("GET", "/api/Candidate/1", xsrfHeader, "", 200, `{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}`) + // Get all the parties + do("GET", "/api/Candidate/", xsrfHeader, "", 200, `[{"ID":1,"CandidateListID":1,"FullName":"Candidate","Rank":1,"CommunityCounseller":true,"Birthdate":"2020-06-28","PotentialIncompatibility":false,"Refused":false,"Removed":false}]`) + // Update a Candidate should fail with 405 + do("PUT", "/api/Candidate/1", xsrfHeader, `{"ID":1,"CandidateListID":1,"FullName":"CandidateTest","Rank":2,"CommunityCounseller":false,"Birthdate":"2020-02-28","PotentialIncompatibility":true,"Refused":false,"Removed":false}`, 405, `You're not authorize to execute this method on this ressource.`) + // Delete a Candidate should fail with 405 + do("DELETE", "/api/Candidate/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`) + // Add deskround to capturer should fail with 405 do("POST", "/api/CapturerDeskRound", xsrfHeader, `{"CapturerID":1,"DeskRoundID":1}`, 405, `You're not authorize to execute this method on this ressource.`) // Remove DeskRound to capturer