2020-03-04 11:07:12 +00:00
package main
import (
2022-07-09 17:42:00 +00:00
"log"
2020-03-04 11:07:12 +00:00
"net/http"
"strconv"
2022-09-02 12:08:03 +00:00
"sync"
2020-03-08 01:18:32 +00:00
"time"
2020-03-04 11:07:12 +00:00
2022-07-09 17:42:00 +00:00
"github.com/gin-gonic/gin"
2020-11-20 14:55:54 +00:00
"github.com/russross/blackfriday/v2"
2020-03-04 11:07:12 +00:00
)
2022-09-02 12:08:03 +00:00
var (
_questions_cache = map [ int64 ] * Question { }
_questions_cache_mutex = sync . RWMutex { }
)
2022-07-09 17:42:00 +00:00
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 )
}
u := c . MustGet ( "LoggedUser" ) . ( * User )
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 {
c . JSON ( http . StatusOK , questions )
}
} else {
2022-11-19 10:41:35 +00:00
if s . Direct != nil && ! u . IsAdmin {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Not accessible" } )
return
2020-03-08 00:06:44 +00:00
}
2020-03-08 01:18:32 +00:00
if s . StartAvailability . After ( time . Now ( ) ) && ! u . IsAdmin {
2022-07-09 17:42:00 +00:00
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Not accessible yet" } )
return
2020-03-08 01:18:32 +00:00
}
2020-03-08 00:06:44 +00:00
2022-07-09 17:42:00 +00:00
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
2022-02-28 18:00:30 +00:00
} else {
2022-07-09 17:42:00 +00:00
c . JSON ( http . StatusOK , questions )
2022-02-28 18:00:30 +00:00
}
2022-07-09 17:42:00 +00:00
}
} )
questionsRoutes := router . Group ( "/questions/:qid" )
questionsRoutes . Use ( questionHandler )
2022-12-02 10:47:14 +00:00
questionsRoutes . Use ( questionUserAccessHandler )
2022-07-09 17:42:00 +00:00
questionsRoutes . GET ( "" , func ( c * gin . Context ) {
2022-11-19 10:41:35 +00:00
c . JSON ( http . StatusOK , c . MustGet ( "question" ) . ( * Question ) )
2022-07-09 17:42:00 +00:00
} )
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
}
2020-03-04 11:07:12 +00:00
var new Question
2022-07-09 17:42:00 +00:00
if err := c . ShouldBindJSON ( & new ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
2020-03-04 11:07:12 +00:00
}
2022-07-09 17:42:00 +00:00
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 )
2022-12-02 10:47:14 +00:00
questionsRoutes . Use ( questionUserAccessHandler )
2022-07-09 17:42:00 +00:00
questionsRoutes . PUT ( "" , func ( c * gin . Context ) {
current := c . MustGet ( "question" ) . ( * Question )
2020-03-04 11:07:12 +00:00
var new Question
2022-07-09 17:42:00 +00:00
if err := c . ShouldBindJSON ( & new ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
2020-03-04 11:07:12 +00:00
}
new . Id = current . Id
2022-07-09 17:42:00 +00:00
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 )
2020-03-04 11:07:12 +00:00
2022-07-09 17:42:00 +00:00
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
2020-03-04 11:07:12 +00:00
}
2022-07-09 17:42:00 +00:00
c . JSON ( http . StatusOK , nil )
} )
declareAPIAdminCorrectionsRoutes ( questionsRoutes )
declareAPIAdminProposalsRoutes ( questionsRoutes )
declareAPIAdminResponsesRoutes ( questionsRoutes )
}
func declareAPIAdminUserQuestionsRoutes ( router * gin . RouterGroup ) {
questionsRoutes := router . Group ( "/questions/:qid" )
questionsRoutes . Use ( questionHandler )
2022-12-02 10:47:14 +00:00
questionsRoutes . Use ( questionUserAccessHandler )
2022-07-09 17:42:00 +00:00
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
2020-03-04 11:07:12 +00:00
}
2022-07-09 17:42:00 +00:00
c . JSON ( http . StatusOK , score )
} )
2020-03-04 11:07:12 +00:00
}
2022-07-09 17:42:00 +00:00
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
2020-03-04 11:07:12 +00:00
2022-07-09 17:42:00 +00:00
if survey == nil {
question , err = getQuestion ( qid )
if err != nil {
c . AbortWithStatusJSON ( http . StatusNotFound , gin . H { "errmsg" : "Question not found" } )
return
}
2022-12-02 10:47:14 +00:00
} 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 ( )
}
func questionUserAccessHandler ( c * gin . Context ) {
var survey * Survey
if s , ok := c . Get ( "survey" ) ; ok {
survey = s . ( * Survey )
}
2022-11-19 10:41:35 +00:00
2022-12-02 10:47:14 +00:00
u := c . MustGet ( "LoggedUser" ) . ( * User )
question := c . MustGet ( "question" ) . ( * Question )
if survey == nil {
2022-11-19 10:41:35 +00:00
s , err := getSurvey ( int ( question . 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
}
survey = s
2020-03-04 11:07:12 +00:00
}
2022-07-09 17:42:00 +00:00
2022-11-19 10:41:35 +00:00
if ! u . IsAdmin && ( ! survey . checkUserAccessToSurvey ( u ) || ( survey . Direct != nil && * survey . Direct != question . Id ) ) {
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Not authorized" } )
return
}
if ! u . IsAdmin && survey . StartAvailability . After ( time . Now ( ) ) {
c . AbortWithStatusJSON ( http . StatusForbidden , gin . H { "errmsg" : "Not accessible yet" } )
return
}
2022-07-09 17:42:00 +00:00
c . Next ( )
2020-03-04 11:07:12 +00:00
}
type Question struct {
2020-10-31 17:06:39 +00:00
Id int64 ` json:"id" `
IdSurvey int64 ` json:"id_survey" `
Title string ` json:"title" `
Description string ` json:"description" `
DescriptionRaw string ` json:"desc_raw,omitempty" `
Placeholder string ` json:"placeholder,omitempty" `
Kind string ` json:"kind" `
2020-03-04 11:07:12 +00:00
}
2022-07-09 17:42:00 +00:00
func getQuestions ( ) ( questions [ ] * Question , err error ) {
2020-03-04 11:07:12 +00:00
if rows , errr := DBQuery ( "SELECT id_question, id_survey, title, description, placeholder, kind FROM survey_quests" ) ; errr != nil {
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var q Question
2020-10-31 17:06:39 +00:00
if err = rows . Scan ( & q . Id , & q . IdSurvey , & q . Title , & q . DescriptionRaw , & q . Placeholder , & q . Kind ) ; err != nil {
2020-03-04 11:07:12 +00:00
return
}
2020-10-31 17:06:39 +00:00
q . Description = string ( blackfriday . Run ( [ ] byte ( q . DescriptionRaw ) ) )
2022-07-09 17:42:00 +00:00
questions = append ( questions , & q )
2020-03-04 11:07:12 +00:00
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
2022-07-09 17:42:00 +00:00
func ( s * Survey ) GetQuestions ( ) ( questions [ ] * Question , err error ) {
2020-03-04 11:07:12 +00:00
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 {
defer rows . Close ( )
for rows . Next ( ) {
var q Question
2020-10-31 17:06:39 +00:00
if err = rows . Scan ( & q . Id , & q . IdSurvey , & q . Title , & q . DescriptionRaw , & q . Placeholder , & q . Kind ) ; err != nil {
2020-03-04 11:07:12 +00:00
return
}
2020-10-31 17:06:39 +00:00
q . Description = string ( blackfriday . Run ( [ ] byte ( q . DescriptionRaw ) ) )
2022-07-09 17:42:00 +00:00
questions = append ( questions , & q )
2020-03-04 11:07:12 +00:00
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
2022-07-09 17:42:00 +00:00
func getQuestion ( id int ) ( q * Question , err error ) {
2022-09-02 12:08:03 +00:00
_questions_cache_mutex . RLock ( )
question , ok := _questions_cache [ int64 ( id ) ]
_questions_cache_mutex . RUnlock ( )
if ok {
return question , nil
}
2022-07-09 17:42:00 +00:00
q = new ( Question )
2020-10-31 17:06:39 +00:00
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 ) ) )
2022-09-02 12:08:03 +00:00
_questions_cache_mutex . Lock ( )
_questions_cache [ int64 ( id ) ] = q
_questions_cache_mutex . Unlock ( )
2020-03-04 11:07:12 +00:00
return
}
2022-07-09 17:42:00 +00:00
func ( s * Survey ) GetQuestion ( id int ) ( q * Question , err error ) {
2022-09-02 12:08:03 +00:00
_questions_cache_mutex . RLock ( )
question , ok := _questions_cache [ int64 ( id ) ]
_questions_cache_mutex . RUnlock ( )
if ok {
return question , nil
}
2022-07-09 17:42:00 +00:00
q = new ( Question )
2020-10-31 17:06:39 +00:00
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 ) ) )
2022-09-02 12:08:03 +00:00
_questions_cache_mutex . Lock ( )
_questions_cache [ int64 ( id ) ] = q
_questions_cache_mutex . Unlock ( )
2020-03-04 11:07:12 +00:00
return
}
2022-07-09 17:42:00 +00:00
func ( s * Survey ) NewQuestion ( title string , description string , placeholder string , kind string ) ( * Question , error ) {
2020-03-04 11:07:12 +00:00
if res , err := DBExec ( "INSERT INTO survey_quests (id_survey, title, description, placeholder, kind) VALUES (?, ?, ?, ?, ?)" , s . Id , title , description , placeholder , kind ) ; err != nil {
2022-07-09 17:42:00 +00:00
return nil , err
2020-03-04 11:07:12 +00:00
} else if qid , err := res . LastInsertId ( ) ; err != nil {
2022-07-09 17:42:00 +00:00
return nil , err
2020-03-04 11:07:12 +00:00
} else {
2022-07-09 17:42:00 +00:00
return & Question { qid , s . Id , title , string ( blackfriday . Run ( [ ] byte ( description ) ) ) , description , placeholder , kind } , nil
2020-03-04 11:07:12 +00:00
}
}
2022-07-09 17:42:00 +00:00
func ( q * Question ) GetSurvey ( ) ( * Survey , error ) {
2020-03-04 11:07:12 +00:00
return getSurvey ( int ( q . IdSurvey ) )
}
2022-07-09 17:42:00 +00:00
func ( q * Question ) Update ( ) ( * Question , error ) {
2020-10-31 17:06:39 +00:00
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 {
2022-07-09 17:42:00 +00:00
return nil , err
2020-03-04 11:07:12 +00:00
} else {
2022-09-16 10:14:26 +00:00
q . Description = string ( blackfriday . Run ( [ ] byte ( q . DescriptionRaw ) ) )
2022-09-02 12:08:03 +00:00
_questions_cache_mutex . Lock ( )
_questions_cache [ q . Id ] = q
_questions_cache_mutex . Unlock ( )
2020-03-04 11:07:12 +00:00
return q , err
}
}
2022-07-09 17:42:00 +00:00
func ( q * Question ) Delete ( ) ( int64 , error ) {
2020-03-04 11:07:12 +00:00
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 {
return 0 , err
} else {
2022-09-02 12:08:03 +00:00
_questions_cache_mutex . Lock ( )
delete ( _questions_cache , q . Id )
_questions_cache_mutex . Unlock ( )
2020-03-04 11:07:12 +00:00
return nb , err
}
}
func ClearQuestions ( ) ( int64 , error ) {
if res , err := DBExec ( "DELETE FROM survey_quests" ) ; err != nil {
return 0 , err
} else if nb , err := res . RowsAffected ( ) ; err != nil {
return 0 , err
} else {
2022-09-02 12:08:03 +00:00
_questions_cache_mutex . Lock ( )
_questions_cache = map [ int64 ] * Question { }
_questions_cache_mutex . Unlock ( )
2020-03-04 11:07:12 +00:00
return nb , err
}
}