Use gin-gonic instead of httprouter
This commit is contained in:
parent
7c719d9fd5
commit
a203cdc36a
22 changed files with 1668 additions and 1392 deletions
310
questions.go
310
questions.go
|
@ -1,132 +1,200 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/russross/blackfriday/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.GET("/api/questions", apiHandler(
|
||||
func(httprouter.Params, []byte) HTTPResponse {
|
||||
return formatApiResponse(getQuestions())
|
||||
}, adminRestricted))
|
||||
router.GET("/api/surveys/:sid/questions", apiAuthHandler(surveyAuthHandler(
|
||||
func(s Survey, u *User, _ []byte) HTTPResponse {
|
||||
if !s.Shown && !u.IsAdmin {
|
||||
return APIErrorResponse{err: errors.New("Not accessible")}
|
||||
}
|
||||
if s.StartAvailability.After(time.Now()) && !u.IsAdmin {
|
||||
return APIErrorResponse{status: http.StatusPaymentRequired, err: errors.New("Not available yet")}
|
||||
}
|
||||
return formatApiResponse(s.GetQuestions())
|
||||
}), loggedUser))
|
||||
router.POST("/api/surveys/:sid/questions", apiAuthHandler(surveyAuthHandler(func(s Survey, u *User, body []byte) HTTPResponse {
|
||||
if !s.Shown && !u.IsAdmin {
|
||||
return APIErrorResponse{err: errors.New("Not accessible")}
|
||||
func declareAPIAuthQuestionsRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/questions", func(c *gin.Context) {
|
||||
var s *Survey
|
||||
if survey, ok := c.Get("survey"); ok {
|
||||
s = survey.(*Survey)
|
||||
}
|
||||
|
||||
var new Question
|
||||
if err := json.Unmarshal(body, &new); err != nil {
|
||||
return APIErrorResponse{err: err}
|
||||
}
|
||||
u := c.MustGet("LoggedUser").(*User)
|
||||
|
||||
return formatApiResponse(s.NewQuestion(new.Title, new.DescriptionRaw, new.Placeholder, new.Kind))
|
||||
}), adminRestricted))
|
||||
router.GET("/api/questions/:qid", apiAuthHandler(questionAuthHandler(
|
||||
func(q Question, u *User, _ []byte) HTTPResponse {
|
||||
if u.IsAdmin {
|
||||
return APIResponse{q}
|
||||
} else if s, err := getSurvey(int(q.IdSurvey)); err != nil {
|
||||
return APIErrorResponse{err: err}
|
||||
} else if s.Shown || (s.Direct != nil && *s.Direct == q.Id) {
|
||||
return APIResponse{q}
|
||||
if s == nil {
|
||||
if !u.IsAdmin {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Permission denied"})
|
||||
return
|
||||
}
|
||||
|
||||
if questions, err := getQuestions(); err != nil {
|
||||
log.Println("Unable to getQuestions:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve questions. Please try again later."})
|
||||
return
|
||||
} else {
|
||||
return APIErrorResponse{err: fmt.Errorf("Not authorized"), status: http.StatusForbidden}
|
||||
}
|
||||
}), loggedUser))
|
||||
router.GET("/api/surveys/:sid/questions/:qid", apiHandler(questionHandler(
|
||||
func(s Question, _ []byte) HTTPResponse {
|
||||
return APIResponse{s}
|
||||
}), adminRestricted))
|
||||
router.PUT("/api/questions/:qid", apiHandler(questionHandler(func(current Question, body []byte) HTTPResponse {
|
||||
var new Question
|
||||
if err := json.Unmarshal(body, &new); err != nil {
|
||||
return APIErrorResponse{err: err}
|
||||
}
|
||||
|
||||
new.Id = current.Id
|
||||
return formatApiResponse(new.Update())
|
||||
}), adminRestricted))
|
||||
router.PUT("/api/surveys/:sid/questions/:qid", apiHandler(questionHandler(func(current Question, body []byte) HTTPResponse {
|
||||
var new Question
|
||||
if err := json.Unmarshal(body, &new); err != nil {
|
||||
return APIErrorResponse{err: err}
|
||||
}
|
||||
|
||||
new.Id = current.Id
|
||||
return formatApiResponse(new.Update())
|
||||
}), adminRestricted))
|
||||
router.DELETE("/api/questions/:qid", apiHandler(questionHandler(
|
||||
func(q Question, _ []byte) HTTPResponse {
|
||||
return formatApiResponse(q.Delete())
|
||||
}), adminRestricted))
|
||||
router.DELETE("/api/surveys/:sid/questions/:qid", apiHandler(questionHandler(
|
||||
func(q Question, _ []byte) HTTPResponse {
|
||||
return formatApiResponse(q.Delete())
|
||||
}), adminRestricted))
|
||||
}
|
||||
|
||||
func questionHandler(f func(Question, []byte) HTTPResponse) func(httprouter.Params, []byte) HTTPResponse {
|
||||
return func(ps httprouter.Params, body []byte) HTTPResponse {
|
||||
var survey *Survey = nil
|
||||
|
||||
if sid, err := strconv.Atoi(string(ps.ByName("sid"))); err == nil {
|
||||
if s, err := getSurvey(sid); err == nil {
|
||||
survey = &s
|
||||
}
|
||||
}
|
||||
|
||||
if qid, err := strconv.Atoi(string(ps.ByName("qid"))); err != nil {
|
||||
return APIErrorResponse{err: err}
|
||||
} else if survey == nil {
|
||||
if question, err := getQuestion(qid); err != nil {
|
||||
return APIErrorResponse{err: err}
|
||||
} else {
|
||||
return f(question, body)
|
||||
c.JSON(http.StatusOK, questions)
|
||||
}
|
||||
} else {
|
||||
if question, err := survey.GetQuestion(qid); err != nil {
|
||||
return APIErrorResponse{err: err}
|
||||
if !s.Shown && !u.IsAdmin {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible"})
|
||||
return
|
||||
}
|
||||
if s.StartAvailability.After(time.Now()) && !u.IsAdmin {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible yet"})
|
||||
return
|
||||
}
|
||||
|
||||
if questions, err := s.GetQuestions(); err != nil {
|
||||
log.Println("Unable to GetQuestions:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve questions. Please try again later."})
|
||||
return
|
||||
} else {
|
||||
return f(question, body)
|
||||
c.JSON(http.StatusOK, questions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
func questionAuthHandler(f func(Question, *User, []byte) HTTPResponse, access ...func(*User, *Question) error) func(*User, httprouter.Params, []byte) HTTPResponse {
|
||||
return func(u *User, ps httprouter.Params, body []byte) HTTPResponse {
|
||||
return questionHandler(func(q Question, body []byte) HTTPResponse {
|
||||
// Check access limitation
|
||||
for _, a := range access {
|
||||
if err := a(u, &q); err != nil {
|
||||
return APIErrorResponse{
|
||||
status: http.StatusForbidden,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
questionsRoutes := router.Group("/questions/:qid")
|
||||
questionsRoutes.Use(questionHandler)
|
||||
|
||||
questionsRoutes.GET("", func(c *gin.Context) {
|
||||
q := c.MustGet("question").(*Question)
|
||||
u := c.MustGet("LoggedUser").(*User)
|
||||
|
||||
if !u.IsAdmin {
|
||||
s, err := getSurvey(int(q.IdSurvey))
|
||||
if err != nil {
|
||||
log.Println("Unable to getSurvey:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during survey retrieval. Please try again later."})
|
||||
return
|
||||
}
|
||||
|
||||
return f(q, u, body)
|
||||
})(ps, body)
|
||||
if !(s.Shown || (s.Direct != nil && *s.Direct == q.Id)) {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not authorized"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, q)
|
||||
})
|
||||
|
||||
declareAPIAuthProposalsRoutes(questionsRoutes)
|
||||
declareAPIAuthQuestionResponsesRoutes(questionsRoutes)
|
||||
}
|
||||
|
||||
func declareAPIAdminQuestionsRoutes(router *gin.RouterGroup) {
|
||||
router.POST("/questions", func(c *gin.Context) {
|
||||
var s *Survey
|
||||
if survey, ok := c.Get("survey"); ok {
|
||||
s = survey.(*Survey)
|
||||
} else {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Survey identifier not defined."})
|
||||
return
|
||||
}
|
||||
|
||||
var new Question
|
||||
if err := c.ShouldBindJSON(&new); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
q, err := s.NewQuestion(new.Title, new.DescriptionRaw, new.Placeholder, new.Kind)
|
||||
if err != nil {
|
||||
log.Println("Unable to NewQuestion:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during question insertion."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, q)
|
||||
})
|
||||
|
||||
questionsRoutes := router.Group("/questions/:qid")
|
||||
questionsRoutes.Use(questionHandler)
|
||||
|
||||
questionsRoutes.PUT("", func(c *gin.Context) {
|
||||
current := c.MustGet("question").(*Question)
|
||||
|
||||
var new Question
|
||||
if err := c.ShouldBindJSON(&new); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
new.Id = current.Id
|
||||
|
||||
if q, err := new.Update(); err != nil {
|
||||
log.Println("Unable to Update question:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during question update."})
|
||||
return
|
||||
} else {
|
||||
c.JSON(http.StatusOK, q)
|
||||
}
|
||||
})
|
||||
questionsRoutes.DELETE("", func(c *gin.Context) {
|
||||
q := c.MustGet("question").(*Question)
|
||||
|
||||
if _, err := q.Delete(); err != nil {
|
||||
log.Println("Unable to Delete question:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to delete the question."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, nil)
|
||||
})
|
||||
|
||||
declareAPIAdminCorrectionsRoutes(questionsRoutes)
|
||||
declareAPIAdminProposalsRoutes(questionsRoutes)
|
||||
declareAPIAdminResponsesRoutes(questionsRoutes)
|
||||
}
|
||||
|
||||
func declareAPIAdminUserQuestionsRoutes(router *gin.RouterGroup) {
|
||||
questionsRoutes := router.Group("/questions/:qid")
|
||||
questionsRoutes.Use(questionHandler)
|
||||
|
||||
questionsRoutes.GET("", func(c *gin.Context) {
|
||||
question := c.MustGet("question").(*Question)
|
||||
user := c.MustGet("user").(*User)
|
||||
|
||||
score, err := question.ComputeScoreQuestion(user)
|
||||
if err != nil {
|
||||
log.Println("Unable to ComputeScoreQuestion:", err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to compute score. Please try again later."})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, score)
|
||||
})
|
||||
}
|
||||
|
||||
func questionHandler(c *gin.Context) {
|
||||
var survey *Survey
|
||||
if s, ok := c.Get("survey"); ok {
|
||||
survey = s.(*Survey)
|
||||
}
|
||||
|
||||
qid, err := strconv.Atoi(string(c.Param("qid")))
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid question identifier."})
|
||||
return
|
||||
}
|
||||
|
||||
var question *Question
|
||||
|
||||
if survey == nil {
|
||||
question, err = getQuestion(qid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Question not found"})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
question, err = survey.GetQuestion(qid)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Question not found"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.Set("question", question)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
type Question struct {
|
||||
|
@ -139,7 +207,7 @@ type Question struct {
|
|||
Kind string `json:"kind"`
|
||||
}
|
||||
|
||||
func getQuestions() (questions []Question, err error) {
|
||||
func getQuestions() (questions []*Question, err error) {
|
||||
if rows, errr := DBQuery("SELECT id_question, id_survey, title, description, placeholder, kind FROM survey_quests"); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
|
@ -151,7 +219,7 @@ func getQuestions() (questions []Question, err error) {
|
|||
return
|
||||
}
|
||||
q.Description = string(blackfriday.Run([]byte(q.DescriptionRaw)))
|
||||
questions = append(questions, q)
|
||||
questions = append(questions, &q)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
|
@ -161,7 +229,7 @@ func getQuestions() (questions []Question, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Survey) GetQuestions() (questions []Question, err error) {
|
||||
func (s *Survey) GetQuestions() (questions []*Question, err error) {
|
||||
if rows, errr := DBQuery("SELECT id_question, id_survey, title, description, placeholder, kind FROM survey_quests WHERE id_survey=?", s.Id); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
|
@ -173,7 +241,7 @@ func (s *Survey) GetQuestions() (questions []Question, err error) {
|
|||
return
|
||||
}
|
||||
q.Description = string(blackfriday.Run([]byte(q.DescriptionRaw)))
|
||||
questions = append(questions, q)
|
||||
questions = append(questions, &q)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
|
@ -183,41 +251,43 @@ func (s *Survey) GetQuestions() (questions []Question, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
func getQuestion(id int) (q Question, err error) {
|
||||
func getQuestion(id int) (q *Question, err error) {
|
||||
q = new(Question)
|
||||
err = DBQueryRow("SELECT id_question, id_survey, title, description, placeholder, kind FROM survey_quests WHERE id_question=?", id).Scan(&q.Id, &q.IdSurvey, &q.Title, &q.DescriptionRaw, &q.Placeholder, &q.Kind)
|
||||
q.Description = string(blackfriday.Run([]byte(q.DescriptionRaw)))
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Survey) GetQuestion(id int) (q Question, err error) {
|
||||
func (s *Survey) GetQuestion(id int) (q *Question, err error) {
|
||||
q = new(Question)
|
||||
err = DBQueryRow("SELECT id_question, id_survey, title, description, placeholder, kind FROM survey_quests WHERE id_question=? AND id_survey=?", id, s.Id).Scan(&q.Id, &q.IdSurvey, &q.Title, &q.DescriptionRaw, &q.Placeholder, &q.Kind)
|
||||
q.Description = string(blackfriday.Run([]byte(q.DescriptionRaw)))
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Survey) NewQuestion(title string, description string, placeholder string, kind string) (Question, error) {
|
||||
func (s *Survey) NewQuestion(title string, description string, placeholder string, kind string) (*Question, error) {
|
||||
if res, err := DBExec("INSERT INTO survey_quests (id_survey, title, description, placeholder, kind) VALUES (?, ?, ?, ?, ?)", s.Id, title, description, placeholder, kind); err != nil {
|
||||
return Question{}, err
|
||||
return nil, err
|
||||
} else if qid, err := res.LastInsertId(); err != nil {
|
||||
return Question{}, err
|
||||
return nil, err
|
||||
} else {
|
||||
return Question{qid, s.Id, title, string(blackfriday.Run([]byte(description))), description, placeholder, kind}, nil
|
||||
return &Question{qid, s.Id, title, string(blackfriday.Run([]byte(description))), description, placeholder, kind}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (q Question) GetSurvey() (Survey, error) {
|
||||
func (q *Question) GetSurvey() (*Survey, error) {
|
||||
return getSurvey(int(q.IdSurvey))
|
||||
}
|
||||
|
||||
func (q Question) Update() (Question, error) {
|
||||
func (q *Question) Update() (*Question, error) {
|
||||
if _, err := DBExec("UPDATE survey_quests SET id_survey = ?, title = ?, description = ?, placeholder = ?, kind = ? WHERE id_question = ?", q.IdSurvey, q.Title, q.DescriptionRaw, q.Placeholder, q.Kind, q.Id); err != nil {
|
||||
return Question{}, err
|
||||
return nil, err
|
||||
} else {
|
||||
return q, err
|
||||
}
|
||||
}
|
||||
|
||||
func (q Question) Delete() (int64, error) {
|
||||
func (q *Question) Delete() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM survey_quests WHERE id_question = ?", q.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
|
|
Reference in a new issue