qa: Use gin
This commit is contained in:
parent
9fd5564410
commit
abdf146fea
13 changed files with 596 additions and 378 deletions
55
qa/api/auth.go
Normal file
55
qa/api/auth.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var Simulator string
|
||||
var TeamsDir string
|
||||
|
||||
func authMiddleware(access ...func(string, int64, *gin.Context) bool) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ficteam := Simulator
|
||||
if t := c.Request.Header.Get("X-FIC-Team"); t != "" {
|
||||
ficteam = t
|
||||
}
|
||||
|
||||
var teamid int64
|
||||
var err error
|
||||
|
||||
if ficteam == "" {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": "Need to authenticate"})
|
||||
return
|
||||
} else if teamid, err = strconv.ParseInt(ficteam, 10, 64); err != nil {
|
||||
if lnk, err := os.Readlink(path.Join(TeamsDir, ficteam)); err != nil {
|
||||
log.Printf("[ERR] Unable to readlink %q: %s\n", path.Join(TeamsDir, ficteam), err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to validate authentication."})
|
||||
return
|
||||
} else if teamid, err = strconv.ParseInt(lnk, 10, 64); err != nil {
|
||||
log.Printf("[ERR] Error during ParseInt team %q: %s\n", lnk, err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to validate authentication."})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check access limitation
|
||||
for _, a := range access {
|
||||
if !a(ficteam, teamid, c) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve corresponding user
|
||||
c.Set("LoggedUser", ficteam)
|
||||
c.Set("LoggedTeam", teamid)
|
||||
|
||||
// We are now ready to continue
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -1,36 +1,58 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.GET("/api/exercices/", apiHandler(listExercices))
|
||||
func declareExercicesRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/exercices", listExercices)
|
||||
|
||||
router.GET("/api/exercices/:eid", apiHandler(exerciceHandler(showExercice)))
|
||||
exercicesRoutes := router.Group("/exercices/:eid")
|
||||
exercicesRoutes.Use(exerciceHandler)
|
||||
exercicesRoutes.GET("", showExercice)
|
||||
}
|
||||
|
||||
func exerciceHandler(f func(QAUser, *fic.Exercice, []byte) (interface{}, error)) func(QAUser, httprouter.Params, []byte) (interface{}, error) {
|
||||
return func(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if eid, err := strconv.ParseInt(string(ps.ByName("eid")), 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := fic.GetExercice(eid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(u, exercice, body)
|
||||
func exerciceHandler(c *gin.Context) {
|
||||
var exercice *fic.Exercice
|
||||
if eid, err := strconv.ParseInt(string(c.Param("eid")), 10, 64); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad exercice identifier."})
|
||||
return
|
||||
} else if exercice, err = fic.GetExercice(eid); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Exercice not found."})
|
||||
return
|
||||
}
|
||||
|
||||
if th, ok := c.Get("theme"); ok {
|
||||
if exercice.IdTheme != th.(*fic.Theme).Id {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Exercice not found."})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.Set("exercice", exercice)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func listExercices(_ QAUser, _ httprouter.Params, body []byte) (interface{}, error) {
|
||||
func listExercices(c *gin.Context) {
|
||||
// List all exercices
|
||||
return fic.GetExercices()
|
||||
exercices, err := fic.GetExercices()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetExercices: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list exercices: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, exercices)
|
||||
}
|
||||
|
||||
func showExercice(_ QAUser, exercice *fic.Exercice, body []byte) (interface{}, error) {
|
||||
return exercice, nil
|
||||
func showExercice(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, c.MustGet("exercice"))
|
||||
}
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
var Simulator string
|
||||
var TeamsDir string
|
||||
|
||||
type QAUser struct {
|
||||
User string `json:"name"`
|
||||
TeamId int64 `json:"id_team"`
|
||||
}
|
||||
|
||||
type DispatchFunction func(QAUser, httprouter.Params, []byte) (interface{}, error)
|
||||
|
||||
func apiHandler(f DispatchFunction) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
ficteam := Simulator
|
||||
if t := r.Header.Get("X-FIC-Team"); t != "" {
|
||||
ficteam = t
|
||||
}
|
||||
|
||||
var teamid int64
|
||||
var err error
|
||||
|
||||
if ficteam == "" {
|
||||
log.Printf("%s 401 \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent())
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
http.Error(w, fmt.Sprintf("{\"errmsg\":\"Need to authenticate.\"}"), http.StatusUnauthorized)
|
||||
return
|
||||
} else if teamid, err = strconv.ParseInt(ficteam, 10, 64); err != nil {
|
||||
if lnk, err := os.Readlink(path.Join(TeamsDir, ficteam)); err != nil {
|
||||
log.Printf("[ERR] Unable to readlink %q: %s\n", path.Join(TeamsDir, ficteam), err)
|
||||
http.Error(w, fmt.Sprintf("{\"errmsg\":\"Unable to validate authentication.\"}"), http.StatusInternalServerError)
|
||||
return
|
||||
} else if teamid, err = strconv.ParseInt(lnk, 10, 64); err != nil {
|
||||
log.Printf("[ERR] Error during ParseInt team %q: %s\n", lnk, err)
|
||||
http.Error(w, fmt.Sprintf("{\"errmsg\":\"Unable to validate authentication.\"}"), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent())
|
||||
|
||||
// Read the body
|
||||
if r.ContentLength < 0 || r.ContentLength > 6553600 {
|
||||
http.Error(w, fmt.Sprintf("{\"errmsg\":\"Request too large or request size unknown\"}"), http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
var body []byte
|
||||
if r.ContentLength > 0 {
|
||||
tmp := make([]byte, 1024)
|
||||
for {
|
||||
n, err := r.Body.Read(tmp)
|
||||
for j := 0; j < n; j++ {
|
||||
body = append(body, tmp[j])
|
||||
}
|
||||
if err != nil || n <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ret interface{}
|
||||
|
||||
ret, err = f(QAUser{ficteam, teamid}, ps, body)
|
||||
|
||||
// Format response
|
||||
resStatus := http.StatusOK
|
||||
if err != nil {
|
||||
ret = map[string]string{"errmsg": err.Error()}
|
||||
resStatus = http.StatusBadRequest
|
||||
log.Println(r.RemoteAddr, resStatus, err.Error())
|
||||
}
|
||||
|
||||
if ret == nil {
|
||||
ret = map[string]string{"errmsg": "Page not found"}
|
||||
resStatus = http.StatusNotFound
|
||||
}
|
||||
|
||||
w.Header().Set("X-FIC-Time", fmt.Sprintf("%f", float64(time.Now().UnixNano()/1000)/1000000))
|
||||
|
||||
if str, found := ret.(string); found {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(resStatus)
|
||||
io.WriteString(w, str)
|
||||
} else if bts, found := ret.([]byte); found {
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
w.Header().Set("Content-Disposition", "attachment")
|
||||
w.Header().Set("Content-Transfer-Encoding", "binary")
|
||||
w.WriteHeader(resStatus)
|
||||
w.Write(bts)
|
||||
} else if j, err := json.Marshal(ret); err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", err), http.StatusInternalServerError)
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(resStatus)
|
||||
w.Write(j)
|
||||
}
|
||||
}
|
||||
}
|
251
qa/api/qa.go
251
qa/api/qa.go
|
@ -1,144 +1,231 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.GET("/api/qa/:eid", apiHandler(exerciceHandler(getExerciceQA)))
|
||||
router.POST("/api/qa/:eid", apiHandler(exerciceHandler(createExerciceQA)))
|
||||
func declareQARoutes(router *gin.RouterGroup) {
|
||||
exercicesRoutes := router.Group("/qa/:eid")
|
||||
exercicesRoutes.Use(exerciceHandler)
|
||||
exercicesRoutes.GET("", getExerciceQA)
|
||||
exercicesRoutes.POST("", createExerciceQA)
|
||||
|
||||
router.PUT("/api/qa/:eid/:qid", apiHandler(qaHandler(updateExerciceQA)))
|
||||
router.DELETE("/api/qa/:eid/:qid", apiHandler(qaHandler(deleteExerciceQA)))
|
||||
qaRoutes := exercicesRoutes.Group("/:qid")
|
||||
qaRoutes.Use(qaHandler)
|
||||
qaRoutes.PUT("", updateExerciceQA)
|
||||
qaRoutes.DELETE("", deleteExerciceQA)
|
||||
qaRoutes.GET("comments", getQAComments)
|
||||
qaRoutes.POST("comments", createQAComment)
|
||||
|
||||
router.GET("/api/qa/:eid/:qid/comments", apiHandler(qaHandler(getQAComments)))
|
||||
router.POST("/api/qa/:eid/:qid/comments", apiHandler(qaHandler(createQAComment)))
|
||||
|
||||
router.DELETE("/api/qa/:eid/:qid/comments/:cid", apiHandler(qaCommentHandler(deleteQAComment)))
|
||||
commentsRoutes := qaRoutes.Group("comments/:cid")
|
||||
commentsRoutes.Use(qaCommentHandler)
|
||||
commentsRoutes.DELETE("", deleteQAComment)
|
||||
}
|
||||
|
||||
func qaHandler(f func(QAUser, *fic.QAQuery, *fic.Exercice, []byte) (interface{}, error)) func(QAUser, httprouter.Params, []byte) (interface{}, error) {
|
||||
return func(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return exerciceHandler(func(u QAUser, exercice *fic.Exercice, _ []byte) (interface{}, error) {
|
||||
if qid, err := strconv.ParseInt(string(ps.ByName("qid")), 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else if query, err := exercice.GetQAQuery(qid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(u, query, exercice, body)
|
||||
}
|
||||
})(u, ps, body)
|
||||
func qaHandler(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
var qa *fic.QAQuery
|
||||
if qid, err := strconv.ParseInt(string(c.Param("qid")), 10, 64); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad QA identifier."})
|
||||
return
|
||||
} else if qa, err = exercice.GetQAQuery(qid); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "QA entry not found."})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("qa", qa)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func qaCommentHandler(f func(QAUser, *fic.QAComment, *fic.QAQuery, *fic.Exercice, []byte) (interface{}, error)) func(QAUser, httprouter.Params, []byte) (interface{}, error) {
|
||||
return func(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return qaHandler(func(u QAUser, query *fic.QAQuery, exercice *fic.Exercice, _ []byte) (interface{}, error) {
|
||||
if cid, err := strconv.ParseInt(string(ps.ByName("cid")), 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else if comment, err := query.GetComment(cid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(u, comment, query, exercice, body)
|
||||
}
|
||||
})(u, ps, body)
|
||||
func qaCommentHandler(c *gin.Context) {
|
||||
qa := c.MustGet("qa").(*fic.QAQuery)
|
||||
var comment *fic.QAComment
|
||||
if cid, err := strconv.ParseInt(string(c.Param("cid")), 10, 64); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad comment identifier."})
|
||||
return
|
||||
} else if comment, err = qa.GetComment(cid); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Comment entry not found."})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("comment", comment)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func getExerciceQA(_ QAUser, exercice *fic.Exercice, body []byte) (interface{}, error) {
|
||||
return exercice.GetQAQueries()
|
||||
func getExerciceQA(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
|
||||
qa, err := exercice.GetQAQueries()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetQAQueries: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list QA entries: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, qa)
|
||||
}
|
||||
|
||||
func createExerciceQA(u QAUser, exercice *fic.Exercice, body []byte) (interface{}, error) {
|
||||
type QAQueryAndComment struct {
|
||||
*fic.QAQuery
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func createExerciceQA(c *gin.Context) {
|
||||
teamid := c.MustGet("LoggedTeam").(int64)
|
||||
ficteam := c.MustGet("LoggedUser").(string)
|
||||
|
||||
// Create a new query
|
||||
var uq *fic.QAQuery
|
||||
if err := json.Unmarshal(body, &uq); err != nil {
|
||||
return nil, err
|
||||
var uq QAQueryAndComment
|
||||
if err := c.ShouldBindJSON(&uq); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if len(uq.State) == 0 {
|
||||
return nil, errors.New("State not filled")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "State not filled"})
|
||||
return
|
||||
}
|
||||
|
||||
if len(uq.Subject) == 0 {
|
||||
if uq.State == "ok" {
|
||||
uq.Subject = "RAS"
|
||||
} else {
|
||||
return nil, errors.New("Subject not filled")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Subject not filled"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if qa, err := exercice.NewQAQuery(uq.Subject, &u.TeamId, u.User, uq.State); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
var uc *fic.QAComment
|
||||
if err := json.Unmarshal(body, &uc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if uc.Content != "" {
|
||||
_, err = qa.AddComment(uc.Content, &u.TeamId, u.User)
|
||||
}
|
||||
|
||||
return qa, err
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
qa, err := exercice.NewQAQuery(uq.Subject, &teamid, ficteam, uq.State)
|
||||
if err != nil {
|
||||
log.Println("Unable to NewQAQuery: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Unable to create the new QA query. Please retry."})
|
||||
return
|
||||
}
|
||||
|
||||
if len(uq.Content) > 0 {
|
||||
_, err = qa.AddComment(uq.Content, &teamid, ficteam)
|
||||
|
||||
if err != nil {
|
||||
log.Println("Unable to AddComment: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "QA entry added successfully, but unable to create the associated comment. Please retry."})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, qa)
|
||||
}
|
||||
|
||||
func updateExerciceQA(u QAUser, query *fic.QAQuery, exercice *fic.Exercice, body []byte) (interface{}, error) {
|
||||
func updateExerciceQA(c *gin.Context) {
|
||||
query := c.MustGet("qa").(*fic.QAQuery)
|
||||
|
||||
var uq *fic.QAQuery
|
||||
if err := json.Unmarshal(body, &uq); err != nil {
|
||||
return nil, err
|
||||
if err := c.ShouldBindJSON(&uq); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
uq.Id = query.Id
|
||||
|
||||
if uq.User != query.User && (uq.IdExercice != query.IdExercice || uq.IdTeam != query.IdTeam || uq.User != query.User || uq.Creation != query.Creation || uq.State != query.State || uq.Subject != query.Subject || uq.Closed != query.Closed) {
|
||||
return nil, errors.New("You can only update your own entry.")
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "You can only update your own entry."})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := uq.Update(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return uq, err
|
||||
}
|
||||
}
|
||||
|
||||
func deleteExerciceQA(u QAUser, query *fic.QAQuery, exercice *fic.Exercice, body []byte) (interface{}, error) {
|
||||
if u.User != query.User {
|
||||
return nil, errors.New("You can only delete your own entry.")
|
||||
_, err := uq.Update()
|
||||
if err != nil {
|
||||
log.Println("Unable to Update QAQuery:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Unable to update the query. Please try again."})
|
||||
return
|
||||
}
|
||||
|
||||
return query.Delete()
|
||||
c.JSON(http.StatusOK, uq)
|
||||
}
|
||||
|
||||
func getQAComments(_ QAUser, query *fic.QAQuery, exercice *fic.Exercice, body []byte) (interface{}, error) {
|
||||
return query.GetComments()
|
||||
func deleteExerciceQA(c *gin.Context) {
|
||||
query := c.MustGet("qa").(*fic.QAQuery)
|
||||
user := c.MustGet("LoggedUser").(string)
|
||||
|
||||
if user != query.User {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "You can only delete your own entry."})
|
||||
return
|
||||
}
|
||||
|
||||
_, err := query.Delete()
|
||||
if err != nil {
|
||||
log.Println("Unable to Delete QAQuery:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to delete the query. Please try again."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func createQAComment(u QAUser, query *fic.QAQuery, exercice *fic.Exercice, body []byte) (interface{}, error) {
|
||||
func getQAComments(c *gin.Context) {
|
||||
query := c.MustGet("qa").(*fic.QAQuery)
|
||||
|
||||
comments, err := query.GetComments()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetComments: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list comments: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, comments)
|
||||
}
|
||||
|
||||
func createQAComment(c *gin.Context) {
|
||||
// Create a new query
|
||||
var uc *fic.QAComment
|
||||
if err := json.Unmarshal(body, &uc); err != nil {
|
||||
return nil, err
|
||||
if err := c.ShouldBindJSON(&uc); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if len(uc.Content) == 0 {
|
||||
return nil, errors.New("Empty comment")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Empty comment."})
|
||||
return
|
||||
}
|
||||
|
||||
return query.AddComment(uc.Content, &u.TeamId, u.User)
|
||||
}
|
||||
teamid := c.MustGet("LoggedTeam").(int64)
|
||||
ficteam := c.MustGet("LoggedUser").(string)
|
||||
|
||||
func deleteQAComment(u QAUser, comment *fic.QAComment, query *fic.QAQuery, exercice *fic.Exercice, body []byte) (interface{}, error) {
|
||||
if u.User != comment.User {
|
||||
return nil, errors.New("You can only delete your own comment.")
|
||||
query := c.MustGet("qa").(*fic.QAQuery)
|
||||
comment, err := query.AddComment(uc.Content, &teamid, ficteam)
|
||||
if err != nil {
|
||||
log.Println("Unable to AddComment: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to add your comment. Please try again later."})
|
||||
return
|
||||
}
|
||||
|
||||
return comment.Delete()
|
||||
c.JSON(http.StatusOK, comment)
|
||||
}
|
||||
|
||||
func deleteQAComment(c *gin.Context) {
|
||||
ficteam := c.MustGet("LoggedUser").(string)
|
||||
comment := c.MustGet("comment").(*fic.QAComment)
|
||||
|
||||
if ficteam != comment.User {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "You can only delete your own comment."})
|
||||
return
|
||||
}
|
||||
|
||||
_, err := comment.Delete()
|
||||
if err != nil {
|
||||
log.Println("Unable to Delete QAComment:", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to delete the comment. Please try again."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusNoContent, nil)
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var router = httprouter.New()
|
||||
func DeclareRoutes(router *gin.RouterGroup) {
|
||||
apiRoutes := router.Group("/api")
|
||||
apiRoutes.Use(authMiddleware())
|
||||
|
||||
func Router() *httprouter.Router {
|
||||
return router
|
||||
declareExercicesRoutes(apiRoutes)
|
||||
declareQARoutes(apiRoutes)
|
||||
declareThemesRoutes(apiRoutes)
|
||||
declareTodoRoutes(apiRoutes)
|
||||
declareVersionRoutes(apiRoutes)
|
||||
}
|
||||
|
|
|
@ -1,64 +1,64 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.GET("/api/themes", apiHandler(listThemes))
|
||||
router.GET("/api/themes.json", apiHandler(exportThemes))
|
||||
func declareThemesRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/themes", listThemes)
|
||||
router.GET("/themes.json", exportThemes)
|
||||
|
||||
router.GET("/api/themes/:thid", apiHandler(themeHandler(showTheme)))
|
||||
themesRoutes := router.Group("/themes/:thid")
|
||||
themesRoutes.Use(themeHandler)
|
||||
themesRoutes.GET("", showTheme)
|
||||
|
||||
router.GET("/api/themes/:thid/exercices", apiHandler(themeHandler(listThemedExercices)))
|
||||
|
||||
router.GET("/api/themes/:thid/exercices/:eid", apiHandler(exerciceHandler(showExercice)))
|
||||
declareExercicesRoutes(themesRoutes)
|
||||
}
|
||||
|
||||
func themeHandler(f func(QAUser, *fic.Theme, []byte) (interface{}, error)) func(QAUser, httprouter.Params, []byte) (interface{}, error) {
|
||||
return func(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if thid, err := strconv.ParseInt(string(ps.ByName("thid")), 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else if theme, err := fic.GetTheme(thid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(u, theme, body)
|
||||
}
|
||||
func themeHandler(c *gin.Context) {
|
||||
var theme *fic.Theme
|
||||
if thid, err := strconv.ParseInt(string(c.Param("thid")), 10, 64); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad theme identifier."})
|
||||
return
|
||||
} else if theme, err = fic.GetTheme(thid); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Theme not found."})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("theme", theme)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func getExercice(args []string) (*fic.Exercice, error) {
|
||||
if tid, err := strconv.ParseInt(string(args[0]), 10, 64); err != nil {
|
||||
return nil, err
|
||||
} else if theme, err := fic.GetTheme(tid); err != nil {
|
||||
return nil, err
|
||||
} else if eid, err := strconv.Atoi(string(args[1])); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return theme.GetExercice(eid)
|
||||
func listThemes(c *gin.Context) {
|
||||
themes, err := fic.GetThemes()
|
||||
if err != nil {
|
||||
log.Println("Unable to GetThemes: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list themes: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, themes)
|
||||
}
|
||||
|
||||
func listThemes(_ QAUser, _ httprouter.Params, _ []byte) (interface{}, error) {
|
||||
return fic.GetThemes()
|
||||
func exportThemes(c *gin.Context) {
|
||||
themes, err := fic.ExportThemes()
|
||||
if err != nil {
|
||||
log.Println("Unable to ExportThemes: ", err.Error())
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to export themes: %s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, themes)
|
||||
}
|
||||
|
||||
func exportThemes(_ QAUser, _ httprouter.Params, _ []byte) (interface{}, error) {
|
||||
return fic.ExportThemes()
|
||||
}
|
||||
|
||||
func showTheme(_ QAUser, theme *fic.Theme, _ []byte) (interface{}, error) {
|
||||
return theme, nil
|
||||
}
|
||||
|
||||
func listThemedExercices(_ QAUser, theme *fic.Theme, _ []byte) (interface{}, error) {
|
||||
return theme.GetExercices()
|
||||
}
|
||||
|
||||
func showThemedExercice(_ QAUser, theme *fic.Theme, exercice *fic.Exercice, body []byte) (interface{}, error) {
|
||||
return exercice, nil
|
||||
func showTheme(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, c.MustGet("theme"))
|
||||
}
|
||||
|
|
208
qa/api/todo.go
208
qa/api/todo.go
|
@ -1,118 +1,176 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.GET("/api/qa_exercices.json", apiHandler(getExerciceTested))
|
||||
router.GET("/api/qa_mywork.json", apiHandler(getQAWork))
|
||||
router.GET("/api/qa_myexercices.json", apiHandler(getQAView))
|
||||
router.POST("/api/qa_my_exercices.json", apiHandler(addQAView))
|
||||
router.GET("/api/qa_work.json", apiHandler(getQATodo))
|
||||
router.POST("/api/qa_work.json", apiHandler(createQATodo))
|
||||
func declareTodoRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/qa_exercices.json", getExerciceTested)
|
||||
router.GET("/qa_mywork.json", getQAWork)
|
||||
router.GET("/qa_myexercices.json", getQAView)
|
||||
router.POST("/qa_my_exercices.json", addQAView)
|
||||
router.GET("/qa_work.json", getQATodo)
|
||||
router.POST("/qa_work.json", createQATodo)
|
||||
}
|
||||
|
||||
type exerciceTested map[int64]string
|
||||
|
||||
func getExerciceTested(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if team, err := fic.GetTeam(u.TeamId); err != nil {
|
||||
return nil, err
|
||||
} else if exercices, err := fic.GetExercices(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ret := exerciceTested{}
|
||||
func getExerciceTested(c *gin.Context) {
|
||||
teamid := c.MustGet("LoggedTeam").(int64)
|
||||
|
||||
for _, exercice := range exercices {
|
||||
if team.HasAccess(exercice) {
|
||||
if t := team.HasSolved(exercice); t != nil {
|
||||
ret[exercice.Id] = "solved"
|
||||
} else if cnt, _ := team.CountTries(exercice); cnt > 0 {
|
||||
ret[exercice.Id] = "tried"
|
||||
} else {
|
||||
ret[exercice.Id] = "access"
|
||||
}
|
||||
team, err := fic.GetTeam(teamid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
exercices, err := fic.GetExercices()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ret := exerciceTested{}
|
||||
|
||||
for _, exercice := range exercices {
|
||||
if team.HasAccess(exercice) {
|
||||
if t := team.HasSolved(exercice); t != nil {
|
||||
ret[exercice.Id] = "solved"
|
||||
} else if cnt, _ := team.CountTries(exercice); cnt > 0 {
|
||||
ret[exercice.Id] = "tried"
|
||||
} else {
|
||||
ret[exercice.Id] = "access"
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
func getQAView(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if team, err := fic.GetTeam(u.TeamId); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return team.GetQAView()
|
||||
func getQAView(c *gin.Context) {
|
||||
teamid := c.MustGet("LoggedTeam").(int64)
|
||||
|
||||
team, err := fic.GetTeam(teamid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
view, err := team.GetQAView()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, view)
|
||||
}
|
||||
|
||||
func getQAWork(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if team, err := fic.GetTeam(u.TeamId); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return team.GetQAQueries()
|
||||
func getQAWork(c *gin.Context) {
|
||||
teamid := c.MustGet("LoggedTeam").(int64)
|
||||
|
||||
team, err := fic.GetTeam(teamid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
queries, err := team.GetQAQueries()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, queries)
|
||||
}
|
||||
|
||||
func getQATodo(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if team, err := fic.GetTeam(u.TeamId); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
todo, err := team.GetQATodo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func getQATodo(c *gin.Context) {
|
||||
teamid := c.MustGet("LoggedTeam").(int64)
|
||||
|
||||
team, err := fic.GetTeam(teamid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
todo, err := team.GetQATodo()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
exercices, err := fic.GetExercices()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
for _, exercice := range exercices {
|
||||
if cnt, _ := team.CountTries(exercice); cnt > 0 {
|
||||
todo = append(todo, &fic.QATodo{0, teamid, exercice.Id})
|
||||
}
|
||||
|
||||
if exercices, err := fic.GetExercices(); err != nil {
|
||||
return todo, nil
|
||||
} else {
|
||||
for _, exercice := range exercices {
|
||||
if cnt, _ := team.CountTries(exercice); cnt > 0 {
|
||||
todo = append(todo, &fic.QATodo{0, team.Id, exercice.Id})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return todo, nil
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, todo)
|
||||
}
|
||||
|
||||
func createQATodo(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if u.User != "nemunaire" {
|
||||
return nil, errors.New("Restricted")
|
||||
func createQATodo(c *gin.Context) {
|
||||
ficteam := c.MustGet("LoggedUser").(string)
|
||||
|
||||
if ficteam != "nemunaire" {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Restricted"})
|
||||
return
|
||||
}
|
||||
|
||||
var ut fic.QATodo
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
if err := c.ShouldBindJSON(&ut); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if team, err := fic.GetTeam(ut.IdTeam); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return team.NewQATodo(ut.IdExercice)
|
||||
team, err := fic.GetTeam(ut.IdTeam)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
todo, err := team.NewQATodo(ut.IdExercice)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, todo)
|
||||
}
|
||||
|
||||
func addQAView(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if u.User != "nemunaire" {
|
||||
return nil, errors.New("Restricted")
|
||||
func addQAView(c *gin.Context) {
|
||||
ficteam := c.MustGet("LoggedUser").(string)
|
||||
|
||||
if ficteam != "nemunaire" {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Restricted"})
|
||||
return
|
||||
}
|
||||
|
||||
var ut fic.QATodo
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
if err := c.ShouldBindJSON(&ut); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if team, err := fic.GetTeam(ut.IdTeam); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return team.NewQAView(ut.IdExercice)
|
||||
team, err := fic.GetTeam(ut.IdTeam)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
view, err := team.NewQAView(ut.IdExercice)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, view)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.GET("/api/version", apiHandler(showVersion))
|
||||
func declareVersionRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/version", showVersion)
|
||||
}
|
||||
|
||||
func showVersion(u QAUser, _ httprouter.Params, body []byte) (interface{}, error) {
|
||||
return map[string]interface{}{"version": 0.1, "auth": u}, nil
|
||||
func showVersion(c *gin.Context) {
|
||||
teamid := c.MustGet("LoggedTeam").(int64)
|
||||
ficteam := c.MustGet("LoggedUser").(string)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"version": 0.2,
|
||||
"auth": map[string]interface{}{
|
||||
"name": ficteam,
|
||||
"id_team": teamid,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
Reference in a new issue