server/admin/api/claim.go

500 lines
14 KiB
Go
Raw Normal View History

2018-01-17 00:21:32 +00:00
package api
import (
"encoding/json"
"fmt"
"io/ioutil"
2022-05-16 09:38:46 +00:00
"log"
"net/http"
"path"
2022-05-16 09:38:46 +00:00
"strconv"
2018-01-17 00:21:32 +00:00
"time"
"srs.epita.fr/fic-server/admin/generation"
2018-01-17 00:21:32 +00:00
"srs.epita.fr/fic-server/libfic"
2022-05-16 09:38:46 +00:00
"github.com/gin-gonic/gin"
2018-01-17 00:21:32 +00:00
)
2022-05-16 09:38:46 +00:00
func declareClaimsRoutes(router *gin.RouterGroup) {
2018-01-17 00:21:32 +00:00
// Tasks
2022-05-16 09:38:46 +00:00
router.GET("/claims", getClaims)
router.POST("/claims", newClaim)
router.DELETE("/claims", clearClaims)
2018-01-17 00:21:32 +00:00
2022-05-16 09:38:46 +00:00
apiClaimsRoutes := router.Group("/claims/:cid")
apiClaimsRoutes.Use(ClaimHandler)
apiClaimsRoutes.GET("", showClaim)
apiClaimsRoutes.PUT("", updateClaim)
apiClaimsRoutes.POST("", addClaimDescription)
apiClaimsRoutes.DELETE("", deleteClaim)
2018-01-17 00:21:32 +00:00
2022-05-16 09:38:46 +00:00
apiClaimsRoutes.GET("/last_update", getClaimLastUpdate)
apiClaimsRoutes.PUT("/descriptions", updateClaimDescription)
2018-01-17 00:21:32 +00:00
// Assignees
2022-05-16 09:38:46 +00:00
router.GET("/claims-assignees", getAssignees)
router.POST("/claims-assignees", newAssignee)
apiClaimAssigneesRoutes := router.Group("/claims-assignees/:aid")
apiClaimAssigneesRoutes.Use(ClaimAssigneeHandler)
router.GET("/claims-assignees/:aid", showClaimAssignee)
router.PUT("/claims-assignees/:aid", updateClaimAssignee)
router.DELETE("/claims-assignees/:aid", deleteClaimAssignee)
}
func declareExerciceClaimsRoutes(router *gin.RouterGroup) {
router.GET("/claims", getExerciceClaims)
}
func declareTeamClaimsRoutes(router *gin.RouterGroup) {
router.GET("/api/teams/:tid/issue.json", func(c *gin.Context) {
team := c.MustGet("team").(*fic.Team)
issues, err := team.MyIssueFile()
if err != nil {
log.Printf("Unable to MyIssueFile(tid=%d): %s", team.Id, err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to generate issues.json."})
return
}
c.JSON(http.StatusOK, issues)
})
router.GET("/claims", getTeamClaims)
}
func ClaimHandler(c *gin.Context) {
cid, err := strconv.ParseInt(string(c.Params.ByName("cid")), 10, 64)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid claim identifier"})
return
}
claim, err := fic.GetClaim(cid)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Requested claim not found"})
return
}
c.Set("claim", claim)
c.Next()
}
func ClaimAssigneeHandler(c *gin.Context) {
aid, err := strconv.ParseInt(string(c.Params.ByName("aid")), 10, 64)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid claim assignee identifier"})
return
}
2018-01-17 00:21:32 +00:00
2022-05-16 09:38:46 +00:00
assignee, err := fic.GetAssignee(aid)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Requested claim-assignee not found"})
return
}
c.Set("claim-assignee", assignee)
c.Next()
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func getClaims(c *gin.Context) {
claims, err := fic.GetClaims()
if err != nil {
log.Println("Unable to getClaims:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claims retrieval."})
return
}
c.JSON(http.StatusOK, claims)
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func getTeamClaims(c *gin.Context) {
team := c.MustGet("team").(*fic.Team)
claims, err := team.GetClaims()
if err != nil {
log.Printf("Unable to GetClaims(tid=%d): %s", team.Id, err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve claim list."})
return
}
c.JSON(http.StatusOK, claims)
}
2022-05-16 09:38:46 +00:00
func getExerciceClaims(c *gin.Context) {
exercice := c.MustGet("exercice").(*fic.Exercice)
claims, err := exercice.GetClaims()
if err != nil {
log.Printf("Unable to GetClaims(eid=%d): %s", exercice.Id, err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve claim list."})
return
}
c.JSON(http.StatusOK, claims)
}
2022-05-16 09:38:46 +00:00
func getClaimLastUpdate(c *gin.Context) {
claim := c.MustGet("claim").(*fic.Claim)
v, err := claim.GetLastUpdate()
if err != nil {
log.Printf("Unable to GetLastUpdate: %s", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim last update retrieval."})
return
}
c.JSON(http.StatusOK, v)
2020-01-24 17:24:20 +00:00
}
2018-01-17 00:21:32 +00:00
type ClaimExported struct {
2021-11-22 14:35:07 +00:00
Id int64 `json:"id"`
Subject string `json:"subject"`
IdTeam *int64 `json:"id_team"`
Team *fic.Team `json:"team"`
IdExercice *int64 `json:"id_exercice"`
Exercice *fic.Exercice `json:"exercice"`
IdAssignee *int64 `json:"id_assignee"`
Assignee *fic.ClaimAssignee `json:"assignee"`
Creation time.Time `json:"creation"`
LastUpdate time.Time `json:"last_update"`
State string `json:"state"`
Priority string `json:"priority"`
Descriptions []*fic.ClaimDescription `json:"descriptions"`
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func showClaim(c *gin.Context) {
claim := c.MustGet("claim").(*fic.Claim)
2018-01-17 00:21:32 +00:00
var e ClaimExported
var err error
if e.Team, err = claim.GetTeam(); err != nil {
2022-05-16 09:38:46 +00:00
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find associated team: %s", err.Error())})
return
2018-01-17 00:21:32 +00:00
}
2020-01-20 08:24:24 +00:00
if e.Exercice, err = claim.GetExercice(); err != nil {
2022-05-16 09:38:46 +00:00
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find associated exercice: %s", err.Error())})
return
2020-01-20 08:24:24 +00:00
}
2018-01-17 00:21:32 +00:00
if e.Assignee, err = claim.GetAssignee(); err != nil {
2022-05-16 09:38:46 +00:00
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find associated assignee: %s", err.Error())})
return
2018-01-17 00:21:32 +00:00
}
if e.Descriptions, err = claim.GetDescriptions(); err != nil {
2022-05-16 09:38:46 +00:00
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find claim's descriptions: %s", err.Error())})
return
2018-01-17 00:21:32 +00:00
}
e.LastUpdate = e.Creation
for _, d := range e.Descriptions {
if d.Date.After(e.LastUpdate) {
e.LastUpdate = d.Date
}
}
e.Id = claim.Id
e.IdAssignee = claim.IdAssignee
e.IdTeam = claim.IdTeam
2020-01-20 08:24:24 +00:00
e.IdExercice = claim.IdExercice
2018-01-17 00:21:32 +00:00
e.Subject = claim.Subject
e.Creation = claim.Creation
e.State = claim.State
e.Priority = claim.Priority
2022-05-16 09:38:46 +00:00
c.JSON(http.StatusOK, e)
2018-01-17 00:21:32 +00:00
}
type ClaimUploaded struct {
fic.Claim
Whoami *int64 `json:"whoami"`
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func newClaim(c *gin.Context) {
2018-01-17 00:21:32 +00:00
var uc ClaimUploaded
2022-05-16 09:38:46 +00:00
err := c.ShouldBindJSON(&uc)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
2018-01-17 00:21:32 +00:00
}
if uc.Subject == "" {
2022-05-16 09:38:46 +00:00
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Claim's subject cannot be empty."})
return
}
2018-01-17 00:21:32 +00:00
var t *fic.Team
if uc.IdTeam != nil {
if team, err := fic.GetTeam(*uc.IdTeam); err != nil {
2022-05-16 09:38:46 +00:00
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated team: %s", err.Error())})
return
2018-01-17 00:21:32 +00:00
} else {
2021-11-22 14:35:07 +00:00
t = team
2018-01-17 00:21:32 +00:00
}
} else {
t = nil
}
2020-01-20 08:24:24 +00:00
var e *fic.Exercice
if uc.IdExercice != nil {
if exercice, err := fic.GetExercice(*uc.IdExercice); err != nil {
2022-05-16 09:38:46 +00:00
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated exercice: %s", err.Error())})
return
2020-01-20 08:24:24 +00:00
} else {
2021-11-22 14:35:07 +00:00
e = exercice
2020-01-20 08:24:24 +00:00
}
} else {
e = nil
}
2018-01-17 00:21:32 +00:00
var a *fic.ClaimAssignee
if uc.IdAssignee != nil {
if assignee, err := fic.GetAssignee(*uc.IdAssignee); err != nil {
2022-05-16 09:38:46 +00:00
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated assignee: %s", err.Error())})
return
2018-01-17 00:21:32 +00:00
} else {
2021-11-22 14:35:07 +00:00
a = assignee
2018-01-17 00:21:32 +00:00
}
} else {
a = nil
}
if uc.Priority == "" {
uc.Priority = "medium"
}
2022-05-16 09:38:46 +00:00
claim, err := fic.NewClaim(uc.Subject, t, e, a, uc.Priority)
if err != nil {
log.Println("Unable to newClaim:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to register new claim"})
return
}
c.JSON(http.StatusOK, claim)
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func clearClaims(c *gin.Context) {
nb, err := fic.ClearClaims()
if err != nil {
log.Printf("Unable to clearClaims: %s", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claims clearing."})
return
}
c.JSON(http.StatusOK, nb)
2018-01-17 00:21:32 +00:00
}
func generateTeamIssuesFile(team fic.Team) error {
if generation.GeneratorSocket == "" {
if my, err := team.MyIssueFile(); err != nil {
return fmt.Errorf("Unable to generate issue FILE (tid=%d): %w", team.Id, err)
} else if j, err := json.Marshal(my); err != nil {
return fmt.Errorf("Unable to encode issues' file JSON: %w", err)
} else if err = ioutil.WriteFile(path.Join(TeamsDir, fmt.Sprintf("%d", team.Id), "issues.json"), j, 0644); err != nil {
return fmt.Errorf("Unable to write issues' file: %w", err)
}
} else {
resp, err := generation.PerformGeneration(fic.GenStruct{Type: fic.GenTeamIssues, TeamId: team.Id})
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
v, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("%s", string(v))
}
}
return nil
}
2022-05-16 09:38:46 +00:00
func addClaimDescription(c *gin.Context) {
claim := c.MustGet("claim").(*fic.Claim)
2018-01-17 00:21:32 +00:00
var ud fic.ClaimDescription
2022-05-16 09:38:46 +00:00
err := c.ShouldBindJSON(&ud)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
assignee, err := fic.GetAssignee(ud.IdAssignee)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated assignee: %s", err.Error())})
return
}
2022-05-16 09:38:46 +00:00
description, err := claim.AddDescription(ud.Content, assignee, ud.Publish)
if err != nil {
log.Println("Unable to addClaimDescription:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to add description"})
return
}
2022-05-16 09:38:46 +00:00
if team, _ := claim.GetTeam(); team != nil {
err = generateTeamIssuesFile(*team)
if err != nil {
log.Println("Unable to generateTeamIssuesFile after addClaimDescription:", err.Error())
}
}
c.JSON(http.StatusOK, description)
}
2022-05-16 09:38:46 +00:00
func updateClaimDescription(c *gin.Context) {
claim := c.MustGet("claim").(*fic.Claim)
var ud fic.ClaimDescription
2022-05-16 09:38:46 +00:00
err := c.ShouldBindJSON(&ud)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
if _, err := ud.Update(); err != nil {
2022-05-16 09:38:46 +00:00
log.Println("Unable to updateClaimDescription:", err.Error())
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during claim description updating."})
return
}
if team, _ := claim.GetTeam(); team != nil {
err = generateTeamIssuesFile(*team)
if err != nil {
log.Println("Unable to generateTeamIssuesFile:", err.Error())
}
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
c.JSON(http.StatusOK, ud)
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func updateClaim(c *gin.Context) {
claim := c.MustGet("claim").(*fic.Claim)
var uc ClaimUploaded
2022-05-16 09:38:46 +00:00
err := c.ShouldBindJSON(&uc)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
2018-01-17 00:21:32 +00:00
}
uc.Id = claim.Id
2022-05-16 09:38:46 +00:00
_, err = uc.Update()
if err != nil {
log.Printf("Unable to updateClaim: %s", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim update."})
return
}
if claim.State != uc.State {
if uc.Whoami != nil {
if assignee, err := fic.GetAssignee(*uc.Whoami); err == nil {
claim.AddDescription(fmt.Sprintf("%s a changé l'état de la tâche vers %q (était %q).", assignee.Name, uc.State, claim.State), assignee, true)
}
}
2022-05-16 09:38:46 +00:00
}
2022-05-16 09:38:46 +00:00
if claim.IdAssignee != uc.IdAssignee {
if uc.Whoami != nil {
if whoami, err := fic.GetAssignee(*uc.Whoami); err == nil {
if uc.IdAssignee != nil {
if assignee, err := fic.GetAssignee(*uc.IdAssignee); err == nil {
if assignee.Id != whoami.Id {
claim.AddDescription(fmt.Sprintf("%s a assigné la tâche à %s.", whoami.Name, assignee.Name), whoami, false)
} else {
claim.AddDescription(fmt.Sprintf("%s s'est assigné la tâche.", assignee.Name), whoami, false)
}
}
2022-05-16 09:38:46 +00:00
} else {
claim.AddDescription(fmt.Sprintf("%s a retiré l'attribution de la tâche.", whoami.Name), whoami, false)
}
}
}
2022-05-16 09:38:46 +00:00
}
2022-05-16 09:38:46 +00:00
if team, _ := claim.GetTeam(); team != nil {
err = generateTeamIssuesFile(*team)
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
c.JSON(http.StatusOK, uc)
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func deleteClaim(c *gin.Context) {
claim := c.MustGet("claim").(*fic.Claim)
if nb, err := claim.Delete(); err != nil {
log.Println("Unable to deleteClaim:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim deletion."})
return
} else {
c.JSON(http.StatusOK, nb)
}
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func getAssignees(c *gin.Context) {
assignees, err := fic.GetAssignees()
if err != nil {
log.Println("Unable to getAssignees:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during assignees retrieval."})
return
}
c.JSON(http.StatusOK, assignees)
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func showClaimAssignee(c *gin.Context) {
c.JSON(http.StatusOK, c.MustGet("claim-assignee").(*fic.ClaimAssignee))
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func newAssignee(c *gin.Context) {
2018-01-17 00:21:32 +00:00
var ua fic.ClaimAssignee
2022-05-16 09:38:46 +00:00
err := c.ShouldBindJSON(&ua)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
assignee, err := fic.NewClaimAssignee(ua.Name)
if err != nil {
log.Println("Unable to newAssignee:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during assignee creation."})
return
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
c.JSON(http.StatusOK, assignee)
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func updateClaimAssignee(c *gin.Context) {
assignee := c.MustGet("claim-assignee").(*fic.ClaimAssignee)
2018-01-17 00:21:32 +00:00
var ua fic.ClaimAssignee
2022-05-16 09:38:46 +00:00
err := c.ShouldBindJSON(&ua)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
2018-01-17 00:21:32 +00:00
}
ua.Id = assignee.Id
if _, err := ua.Update(); err != nil {
2022-05-16 09:38:46 +00:00
log.Println("Unable to updateClaimAssignee:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim assignee update."})
return
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
c.JSON(http.StatusOK, ua)
2018-01-17 00:21:32 +00:00
}
2022-05-16 09:38:46 +00:00
func deleteClaimAssignee(c *gin.Context) {
assignee := c.MustGet("claim-assignee").(*fic.ClaimAssignee)
if _, err := assignee.Delete(); err != nil {
log.Println("Unable to deleteClaimAssignee:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during claim assignee deletion: %s", err.Error())})
return
}
c.JSON(http.StatusOK, true)
2018-01-17 00:21:32 +00:00
}