From 37c6e9874301053ebf649af48b92b47ca8f8ceaf Mon Sep 17 00:00:00 2001
From: Alexis POYEN <apoyen@grandlyon.com>
Date: Tue, 19 May 2020 14:27:52 +0200
Subject: [PATCH] Resolve "Sections API"

---
 internal/models/models.go           |  19 ++---
 internal/models/section.go          | 120 ++++++++++++++++++++++++++++
 internal/rootmux/admin_test.go      |  11 +++
 internal/rootmux/capturer_test.go   |  11 +++
 internal/rootmux/rootmux_test.go    |  33 +++++++-
 internal/rootmux/visualizer_test.go |  11 +++
 6 files changed, 194 insertions(+), 11 deletions(-)
 create mode 100644 internal/models/section.go

diff --git a/internal/models/models.go b/internal/models/models.go
index 675c97b..c5e2076 100644
--- a/internal/models/models.go
+++ b/internal/models/models.go
@@ -62,15 +62,14 @@ type Area struct {
 
 // Section
 type Section struct {
-	ID         uint       `gorm:"primary_key"`
-	CreatedAt  time.Time  `json:"-"`
-	UpdatedAt  time.Time  `json:"-"`
-	DeletedAt  *time.Time `json:"-"`
-	AreaID     uint
-	Name       string
-	SeatNumber uint
-	MapID      string
-	Desks      []Desk
+	ID        uint       `gorm:"primary_key"`
+	CreatedAt time.Time  `json:"-"`
+	UpdatedAt time.Time  `json:"-"`
+	DeletedAt *time.Time `json:"-"`
+	AreaID    uint
+	Name      string
+	MapID     string
+	Desks     []Desk
 }
 
 // Desk
@@ -223,6 +222,8 @@ func (d *DataHandler) ProcessAPI(w http.ResponseWriter, r *http.Request) {
 		d.HandleElection(w, r)
 	case "Area":
 		d.HandleArea(w, r)
+	case "Section":
+		d.HandleSection(w, r)
 	}
 
 }
diff --git a/internal/models/section.go b/internal/models/section.go
new file mode 100644
index 0000000..debe567
--- /dev/null
+++ b/internal/models/section.go
@@ -0,0 +1,120 @@
+package models
+
+import (
+	"encoding/json"
+	"net/http"
+	"strconv"
+	"strings"
+
+	"forge.grandlyon.com/apoyen/elections/internal/auth"
+)
+
+func (d *DataHandler) HandleSection(w http.ResponseWriter, r *http.Request) {
+	id, _ := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/api/Section/"))
+	switch method := r.Method; method {
+	case "GET":
+		switch auth.GetLoggedUserTechnical(w, r).Role {
+		case "ADMIN", "CAPTURER", "VISUALIZER":
+			d.getSection(w, r, id)
+		default:
+			http.Error(w, ErrorRoleOfLoggedUser, http.StatusInternalServerError)
+		}
+	case "POST":
+		switch auth.GetLoggedUserTechnical(w, r).Role {
+		case "ADMIN":
+			d.postSection(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.putSection(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.deleteAreaAdmin(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) getSection(w http.ResponseWriter, r *http.Request, id int) {
+	if id != 0 {
+		var o Section
+		if err := d.db.Preload("Desks").First(&o, id).Error; err != nil {
+			http.Error(w, ErrorIDIsMissing, http.StatusNotFound)
+			return
+		}
+		json.NewEncoder(w).Encode(o)
+	} else {
+		var o []Section
+		d.db.Preload("Desks").Find(&o)
+		json.NewEncoder(w).Encode(o)
+	}
+}
+
+func (d *DataHandler) postSection(w http.ResponseWriter, r *http.Request) {
+	var o Section
+	err := json.NewDecoder(r.Body).Decode(&o)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	// Check that AreaID exist
+	var area Area
+	if err := d.db.First(&area, o.AreaID).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) putSection(w http.ResponseWriter, r *http.Request, id int) {
+	var o Section
+	if err := d.db.Preload("Desks").First(&o, id).Error; err != nil {
+		http.Error(w, ErrorIDIsMissing, http.StatusNotFound)
+		return
+	}
+	var section Section
+	err := json.NewDecoder(r.Body).Decode(&section)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	o.Name = section.Name
+	o.MapID = section.MapID
+	d.db.Save(&o)
+	json.NewEncoder(w).Encode(o)
+
+}
+
+func (d *DataHandler) deleteSection(w http.ResponseWriter, r *http.Request, id int) {
+	if id != 0 {
+		var o Section
+		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/rootmux/admin_test.go b/internal/rootmux/admin_test.go
index 3ab0377..242760e 100644
--- a/internal/rootmux/admin_test.go
+++ b/internal/rootmux/admin_test.go
@@ -51,6 +51,17 @@ func AdminTests(t *testing.T) {
 		// Update an area
 		do("PUT", "/api/Area/1", xsrfHeader, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":7,"MapID":"1"}`, 200, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":7,"MapID":"1","Sections":[]}`)
 
+		// Create a Section
+		do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":null}`)
+		// Get the section
+		do("GET", "/api/Section/1", xsrfHeader, ``, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}`)
+		// Get all the sections
+		do("GET", "/api/Section/", xsrfHeader, ``, 200, `[{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}]`)
+		// Update a section
+		do("PUT", "/api/Section/1", xsrfHeader, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"5"}`, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"5","Desks":[]}`)
+
+		// Delete a section
+		do("DELETE", "/api/Section/1", xsrfHeader, ``, 200, ``)
 		// Delete an area
 		do("DELETE", "/api/Areas/1", xsrfHeader, ``, 200, ``)
 		// Delete an election
diff --git a/internal/rootmux/capturer_test.go b/internal/rootmux/capturer_test.go
index 8936726..c77549f 100644
--- a/internal/rootmux/capturer_test.go
+++ b/internal/rootmux/capturer_test.go
@@ -57,6 +57,17 @@ func CapturerTests(t *testing.T) {
 		// Delete an area should fail with 405
 		do("DELETE", "/api/Area/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
 
+		// Create a section should fail with 405
+		do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 405, `You're not authorize to execute this method on this ressource.`)
+		// Get a section
+		do("GET", "/api/Section/1", xsrfHeader, "", 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}`)
+		// Get all the sections
+		do("GET", "/api/Section/", xsrfHeader, "", 200, `[{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}]`)
+		// Update a section should fail with 405
+		do("PUT", "/api/Section/1", xsrfHeader, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1"}`, 405, `You're not authorize to execute this method on this ressource.`)
+		// Delete a section should fail with 405
+		do("DELETE", "/api/Section/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
+
 	}
 	// Do a in memory login with an known admin
 	do("POST", "/Login", noH, `{"login": "capturer","password": "password"}`, 200, "")
diff --git a/internal/rootmux/rootmux_test.go b/internal/rootmux/rootmux_test.go
index 62facc3..61d6a2f 100644
--- a/internal/rootmux/rootmux_test.go
+++ b/internal/rootmux/rootmux_test.go
@@ -57,9 +57,9 @@ func TestAll(t *testing.T) {
 	appTests(t)
 	resetData(t)
 	AdminTests(t)
-	resetData(t)
+	resetDataWithData(t)
 	CapturerTests(t)
-	resetData(t)
+	resetDataWithData(t)
 	VisualizerTests(t)
 
 	os.RemoveAll("./data")
@@ -122,6 +122,35 @@ func resetData(t *testing.T) {
 	do("GET", "/Logout", noH, "", 200, "Logout OK")
 }
 
+func resetDataWithData(t *testing.T) {
+	os.RemoveAll("./data")
+	os.Mkdir("data", os.ModePerm)
+	os.Create("./data/test.db")
+	os.Link("../../data/users.db", "./data/users.db")
+
+	ts, do, _ := createTester(t)
+	defer ts.Close() // Close the tester
+
+	// Init Data in DB tests
+	init := func() {
+		response := do("GET", "/api/common/WhoAmI", noH, "", 200, "")
+		token := auth.TokenData{}
+		json.Unmarshal([]byte(response), &token)
+		xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
+
+		// Create a capturer
+		do("POST", "/api/Capturer", xsrfHeader, `{"UserID":2,"Name":"Capturer"}`, 200, `{"ID":1,"UserID":2,"Name":"Capturer","DeskRounds":null}`)
+		do("POST", "/api/Capturer", xsrfHeader, `{"UserID":3,"Name":"Capturer"}`, 200, `{"ID":2,"UserID":3,"Name":"Capturer","DeskRounds":null}`)
+		do("POST", "/api/Election", xsrfHeader, `{"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct"}`, 200, `{"ID":1,"Name":"Grand Lyon 2020","BallotType":"metropolitan-direct","Areas":null,"Rounds":null}`)
+		do("POST", "/api/Area", xsrfHeader, `{"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1"}`, 200, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1","Sections":null}`)
+		do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":null}`)
+
+	}
+	do("POST", "/Login", noH, `{"login": "admin","password": "password"}`, 200, "")
+	init()
+	do("GET", "/Logout", noH, "", 200, "Logout OK")
+}
+
 func createTester(t *testing.T) (*httptest.Server, func(method string, url string, header tester.Header, payload string, expectedStatus int, expectedBody string) string, func(method string, url string, header tester.Header, payload string, expectedStatus int, expectedBody string) string) {
 	// Create the server
 	mux := CreateRootMux(1443, "../../web")
diff --git a/internal/rootmux/visualizer_test.go b/internal/rootmux/visualizer_test.go
index 31b5a05..8f43611 100644
--- a/internal/rootmux/visualizer_test.go
+++ b/internal/rootmux/visualizer_test.go
@@ -54,6 +54,17 @@ func VisualizerTests(t *testing.T) {
 		do("PUT", "/api/Area/1", xsrfHeader, `{"ID":1,"ElectionID":1,"Name":"Area 1","SeatNumber":9,"MapID":"1"}`, 405, `You're not authorize to execute this method on this ressource.`)
 		// Delete an area should fail with 405
 		do("DELETE", "/api/Area/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
+
+		// Create a section should fail with 405
+		do("POST", "/api/Section", xsrfHeader, `{"AreaID":1,"Name":"Section 1","MapID":"1"}`, 405, `You're not authorize to execute this method on this ressource.`)
+		// Get a section
+		do("GET", "/api/Section/1", xsrfHeader, "", 200, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}`)
+		// Get all the sections
+		do("GET", "/api/Section/", xsrfHeader, "", 200, `[{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1","Desks":[]}]`)
+		// Update a section should fail with 405
+		do("PUT", "/api/Section/1", xsrfHeader, `{"ID":1,"AreaID":1,"Name":"Section 1","MapID":"1"}`, 405, `You're not authorize to execute this method on this ressource.`)
+		// Delete a section should fail with 405
+		do("DELETE", "/api/Section/1", xsrfHeader, ``, 405, `You're not authorize to execute this method on this ressource.`)
 	}
 	// Do a in memory login with an known admin
 	do("POST", "/Login", noH, `{"login": "visualizer","password": "password"}`, 200, "")
-- 
GitLab