a lot of progress on items as well as get/find/create on project.

This commit is contained in:
2022-04-23 14:07:10 +02:00
parent b2624dc4ab
commit a249110622
24 changed files with 1302 additions and 147 deletions
+30 -1
View File
@@ -20,12 +20,36 @@ func handler(key string, callback func(c *gin.Context) (interface{}, error)) gin
}
resJson := make(map[string]interface{}, 1)
if s := getScope(c); s != nil {
options := ScopeOptions{
StatusLabels: models.StatusLabels,
}
if s.Stats != nil {
options.Stats = make(map[int]models.Stat)
for _, stat := range s.Stats {
options.Stats[stat.ID] = stat
}
}
options.Members = make(map[string]string)
for _, member := range s.Members {
options.Members[member.ID] = member.Name
}
resJson["scopeInfo"] = options
}
resJson[key] = res
c.JSON(200, resJson)
}
}
type ScopeOptions struct {
StatusLabels map[models.Status]string `json:"statusLabels"`
Stats map[int]models.Stat `json:"stats,omitempty"`
Members map[string]string `json:"members,omitempty"`
}
func reqInt(c *gin.Context, key string) (int, error) {
v, err := strconv.Atoi(c.Param(key))
if err != nil {
@@ -66,5 +90,10 @@ func scopeIDMiddleware(db database.Database) gin.HandlerFunc {
}
func getScope(c *gin.Context) *models.Scope {
return c.Request.Context().Value(&scopeIdContextKey).(*models.Scope)
scope := c.Request.Context().Value(&scopeIdContextKey)
if scope == nil {
return nil
}
return scope.(*models.Scope)
}
+111 -18
View File
@@ -12,23 +12,71 @@ import (
func Items(g *gin.RouterGroup, db database.Database) {
g.Use(scopeIDMiddleware(db))
g.GET("/", handler("items", func(c *gin.Context) (res interface{}, err error) {
mode := c.Query("mode")
var fromTime, toTime time.Time
var fromDate, toDate models.Date
if mode != "scheduled" {
if c.Query("to") == "" {
toTime = time.Now()
} else {
toTime, err = time.Parse(time.RFC3339Nano, c.Query("to"))
if err != nil {
return nil, slerrors.BadRequest("Invalid value for to")
}
}
if c.Query("from") == "" {
fromTime = time.Now().Add(-time.Hour * 24 * 30)
} else {
fromTime, err = time.Parse(time.RFC3339Nano, c.Query("from"))
if err != nil {
return nil, slerrors.BadRequest("Invalid value for from")
}
}
if toTime.After(toTime) {
return nil, slerrors.BadRequest("the flow of time itself is convoluted, but not so for the database")
}
} else {
if c.Query("to") == "" {
toDate, _ = models.ParseDate(time.Now().Add(time.Hour * 24 * 7).Format("2006-01-02"))
} else {
toDate, err = models.ParseDate(c.Query("to"))
if err != nil {
return nil, slerrors.BadRequest("Invalid value for to")
}
}
if c.Query("from") == "" {
fromDate, _ = models.ParseDate(time.Now().Format("2006-01-02"))
} else {
fromDate, err = models.ParseDate(c.Query("from"))
if err != nil {
return nil, slerrors.BadRequest("Invalid value for from")
}
}
}
switch mode {
case "", "created":
return db.Items(getScope(c).ID).ListCreated(c.Request.Context(), fromTime, toTime)
case "acquired":
return db.Items(getScope(c).ID).ListAcquired(c.Request.Context(), fromTime, toTime)
case "loose":
return db.Items(getScope(c).ID).ListLoose(c.Request.Context(), fromTime, toTime)
case "scheduled":
return db.Items(getScope(c).ID).ListScheduled(c.Request.Context(), fromDate, toDate)
default:
return nil, slerrors.BadRequest("unknown mode")
}
}))
g.GET("/:id", handler("item", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
item, err := db.Items(getScope(c).ID).Find(c.Request.Context(), id)
if err != nil {
return nil, err
}
for _, member := range getScope(c).Members {
if member.ID == item.OwnerID {
item.Owner = &member
}
}
return item, nil
return db.Items(getScope(c).ID).Find(c.Request.Context(), id)
}))
g.POST("/", handler("item", func(c *gin.Context) (interface{}, error) {
@@ -48,10 +96,6 @@ func Items(g *gin.RouterGroup, db database.Database) {
} else if getScope(c).HasMember(item.OwnerID) {
return nil, slerrors.Forbidden("Owner is not part of scope.")
}
if item.ProjectRequirementID != nil {
// TODO: Ensure project requirement exists in scope.
return nil, slerrors.Forbidden("TODO: check project requirement ID.")
}
for _, stat := range item.Stats {
if getScope(c).Stat(stat.ID) == nil {
return nil, slerrors.Forbidden("One or more stats are not part of scope.")
@@ -61,12 +105,61 @@ func Items(g *gin.RouterGroup, db database.Database) {
}
}
item, err = db.Items(getScope(c).ID).Create(c.Request.Context(), *item)
return db.Items(getScope(c).ID).Create(c.Request.Context(), *item)
}))
g.PUT("/:id", handler("item", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
item.Owner = getScope(c).Member(item.OwnerID)
return item, err
update := &models.ItemUpdate{}
err = c.BindJSON(update)
if err != nil {
return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error())
}
item, err := db.Items(getScope(c).ID).Find(c.Request.Context(), id)
if err != nil {
return nil, err
}
if update.Name != nil && *update.Name == "" {
return nil, slerrors.BadRequest("Blank item name not allowed")
}
if update.OwnerID != nil || !getScope(c).HasMember(item.OwnerID) {
return nil, slerrors.Forbidden("New item is not part of scope.")
}
for _, stat := range update.Stats {
statRef := getScope(c).Stat(stat.ID)
if stat.Required != 0 && stat.Acquired != 0 && statRef == nil {
return nil, slerrors.Forbidden("One or more stats are not part of the scope.")
}
if !statRef.AllowsAmount(stat.Required) {
return nil, slerrors.Forbidden("One or more stats have a disallowed required amount.")
}
}
return db.Items(getScope(c).ID).Update(c.Request.Context(), *item, *update)
}))
g.DELETE("/:id", handler("item", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
item, err := db.Items(getScope(c).ID).Find(c.Request.Context(), id)
if err != nil {
return nil, err
}
err = db.Items(getScope(c).ID).Delete(c.Request.Context(), *item)
if err != nil {
return nil, err
}
return item, nil
}))
}
+50
View File
@@ -0,0 +1,50 @@
package api
import (
"git.aiterp.net/stufflog3/stufflog3-api/internal/auth"
"git.aiterp.net/stufflog3/stufflog3-api/internal/database"
"git.aiterp.net/stufflog3/stufflog3-api/internal/models"
"git.aiterp.net/stufflog3/stufflog3-api/internal/slerrors"
"github.com/gin-gonic/gin"
"time"
)
func Projects(g *gin.RouterGroup, db database.Database) {
g.Use(scopeIDMiddleware(db))
g.GET("/", handler("projects", func(c *gin.Context) (interface{}, error) {
return db.Projects(getScope(c).ID).List(c.Request.Context())
}))
g.GET("/:id", handler("project", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
return db.Projects(getScope(c).ID).Find(c.Request.Context(), id)
}))
g.POST("/", handler("project", func(c *gin.Context) (interface{}, error) {
project := &models.Project{}
err := c.BindJSON(project)
if err != nil {
return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error())
}
if !project.Status.Valid() {
return nil, slerrors.BadRequest("Unknown/unsupported project status")
}
if project.Name == "" {
return nil, slerrors.BadRequest("Project name cannot be blank")
}
if len(project.Requirements) > 0 {
return nil, slerrors.BadRequest("You cannot submit requirements this way")
}
project.OwnerID = auth.UserID(c)
project.CreatedTime = time.Now()
return db.Projects(getScope(c).ID).Create(c.Request.Context(), *project)
}))
}
+2
View File
@@ -3,6 +3,7 @@ package api
import (
"git.aiterp.net/stufflog3/stufflog3-api/internal/auth"
"git.aiterp.net/stufflog3/stufflog3-api/internal/database"
"git.aiterp.net/stufflog3/stufflog3-api/internal/models"
"git.aiterp.net/stufflog3/stufflog3-api/internal/slerrors"
"github.com/gin-gonic/gin"
)
@@ -25,6 +26,7 @@ func Scopes(g *gin.RouterGroup, db database.Database) {
if !scope.HasMember(auth.UserID(c)) {
return nil, slerrors.NotFound("Scope")
}
scope.StatusLabels = models.StatusLabels
return scope, nil
}))