a lot of progress on items as well as get/find/create on project.
This commit is contained in:
+30
-1
@@ -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
@@ -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
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}))
|
||||
}
|
||||
@@ -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
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user