Browse Source

add stats repo.

mian
Gisle Aune 3 years ago
parent
commit
39bc4fe7e3
  1. 5
      api/common.go
  2. 138
      api/projects.go
  3. 98
      api/stats.go
  4. 1
      cmd/stufflog3-server.go
  5. 12
      internal/database/database.go
  6. 6
      internal/database/mysql/database.go
  7. 294
      internal/database/mysql/mysqlcore/db.go
  8. 21
      internal/database/mysql/mysqlcore/item.sql.go
  9. 57
      internal/database/mysql/mysqlcore/project.sql.go
  10. 99
      internal/database/mysql/mysqlcore/stats.sql.go
  11. 190
      internal/database/mysql/project.go
  12. 9
      internal/database/mysql/queries/item.sql
  13. 15
      internal/database/mysql/queries/project.sql
  14. 0
      internal/database/mysql/queries/sprint.sql
  15. 19
      internal/database/mysql/queries/stats.sql
  16. 144
      internal/database/mysql/stats.go
  17. 32
      internal/models/project.go
  18. 32
      internal/models/stat.go
  19. 2
      internal/sqltypes/nullrawmessage.go

5
api/common.go

@ -9,6 +9,7 @@ import (
"github.com/gin-gonic/gin"
"net/http"
"strconv"
"strings"
)
func handler(key string, callback func(c *gin.Context) (interface{}, error)) gin.HandlerFunc {
@ -26,7 +27,7 @@ func handler(key string, callback func(c *gin.Context) (interface{}, error)) gin
StatusLabels: models.StatusLabels,
}
if s.Stats != nil {
if s.Stats != nil && !strings.Contains(c.FullPath(), "/stats/") {
options.Stats = make(map[int]models.Stat)
for _, stat := range s.Stats {
options.Stats[stat.ID] = stat
@ -73,7 +74,7 @@ func scopeIDMiddleware(db database.Database) gin.HandlerFunc {
return
}
scope, err := db.Scopes().Find(c.Request.Context(), scopeID, c.Request.Method != "GET")
scope, err := db.Scopes().Find(c.Request.Context(), scopeID, strings.Contains(c.FullPath(), "/stats/") || c.Request.Method != "GET")
if err != nil || !scope.HasMember(auth.UserID(c)) {
c.AbortWithStatusJSON(http.StatusUnauthorized, slerrors.ErrorResponse{
Code: http.StatusNotFound,

138
api/projects.go

@ -47,4 +47,142 @@ func Projects(g *gin.RouterGroup, db database.Database) {
return db.Projects(getScope(c).ID).Create(c.Request.Context(), *project)
}))
g.PUT("/:id", handler("project", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
update := &models.ProjectUpdate{}
err = c.BindJSON(update)
if err != nil {
return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error())
}
project, err := db.Projects(getScope(c).ID).Find(c.Request.Context(), id)
if err != nil {
return nil, err
}
return db.Projects(getScope(c).ID).Update(c.Request.Context(), *project, *update)
}))
g.POST("/:id/requirements", handler("project", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
requirement := &models.ProjectRequirement{}
err = c.BindJSON(requirement)
if err != nil {
return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error())
}
if requirement.Name == "" {
return nil, slerrors.BadRequest("Requirement name cannot be empty.")
}
if !requirement.Status.Valid() {
return nil, slerrors.BadRequest("Bad requirement status.")
}
for _, stat := range requirement.Stats {
if getScope(c).Stat(stat.ID) == nil {
return nil, slerrors.Forbidden("One or more stats are not part of scope.")
}
if stat.Required < 0 {
return nil, slerrors.BadRequest("-1 stats are not allowed when creating project requirements.")
}
}
project, err := db.Projects(getScope(c).ID).Find(c.Request.Context(), id)
if err != nil {
return nil, err
}
return db.Projects(getScope(c).ID).AddRequirement(c.Request.Context(), project.ProjectEntry, *requirement)
}))
g.PUT("/:id/requirements/:rid", handler("project", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
rid, err := reqInt(c, "rid")
if err != nil {
return nil, err
}
update := &models.ProjectRequirementUpdate{}
err = c.BindJSON(update)
if err != nil {
return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error())
}
if update.Name != nil && *update.Name == "" {
return nil, slerrors.BadRequest("Requirement name cannot be empty.")
}
if update.Status != nil && !update.Status.Valid() {
return nil, slerrors.BadRequest("Bad requirement status.")
}
for _, stat := range update.Stats {
if getScope(c).Stat(stat.ID) == nil {
return nil, slerrors.Forbidden("One or more stats are not part of scope.")
}
}
project, err := db.Projects(getScope(c).ID).Find(c.Request.Context(), id)
if err != nil {
return nil, err
}
requirement := project.Requirement(rid)
if requirement == nil {
return nil, slerrors.NotFound("Project requirement")
}
return db.Projects(getScope(c).ID).UpdateRequirement(c.Request.Context(), project.ProjectEntry, *requirement, *update)
}))
g.DELETE("/:id/requirements/:rid", handler("project", func(c *gin.Context) (interface{}, error) {
deleteItems := c.Query("deleteItems") == "true"
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
rid, err := reqInt(c, "rid")
if err != nil {
return nil, err
}
project, err := db.Projects(getScope(c).ID).Find(c.Request.Context(), id)
if err != nil {
return nil, err
}
requirement := project.Requirement(rid)
if requirement == nil {
return nil, slerrors.NotFound("Project requirement")
}
return db.Projects(getScope(c).ID).DeleteRequirement(c.Request.Context(), project.ProjectEntry, *requirement, deleteItems)
}))
g.DELETE("/:id", handler("project", func(c *gin.Context) (interface{}, error) {
deleteItems := c.Query("deleteItems") == "true"
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
project, err := db.Projects(getScope(c).ID).Find(c.Request.Context(), id)
if err != nil {
return nil, err
}
err = db.Projects(getScope(c).ID).Delete(c.Request.Context(), project.ProjectEntry, deleteItems)
if err != nil {
return nil, err
}
return project, nil
}))
}

98
api/stats.go

@ -0,0 +1,98 @@
package api
import (
"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"
)
func Stats(g *gin.RouterGroup, db database.Database) {
g.Use(scopeIDMiddleware(db))
g.GET("/", handler("stats", func(c *gin.Context) (interface{}, error) {
return getScope(c).Stats, nil
}))
g.GET("/:id", handler("stat", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
stat := getScope(c).Stat(id)
if stat == nil {
return nil, slerrors.NotFound("Stat")
}
return stat, nil
}))
g.POST("/", handler("stat", func(c *gin.Context) (interface{}, error) {
stat := &models.Stat{}
err := c.BindJSON(stat)
if err != nil {
return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error())
}
if stat.Name == "" {
return nil, slerrors.BadRequest("Project name cannot be blank")
}
if stat.Weight < 0 {
return nil, slerrors.BadRequest("Negative weight not allowed")
}
for key, value := range stat.AllowedAmounts {
if value <= 0 {
return nil, slerrors.BadRequest("Invalid allowed amount: " + key + " (0/-1 only allowed when updating to delete them)")
}
}
return db.Stats(getScope(c).ID).Create(c.Request.Context(), *stat)
}))
g.PUT("/:id", handler("stat", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
update := &models.StatUpdate{}
err = c.BindJSON(update)
if err != nil {
return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error())
}
stat := getScope(c).Stat(id)
if stat == nil {
return nil, slerrors.NotFound("Stat")
}
if update.Name != nil && *update.Name == "" {
return nil, slerrors.BadRequest("Project name cannot be blank")
}
if update.Weight != nil && *update.Weight < 0 {
return nil, slerrors.BadRequest("Negative weight not allowed")
}
return db.Stats(getScope(c).ID).Update(c.Request.Context(), *stat, *update)
}))
g.DELETE("/:id", handler("stat", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "id")
if err != nil {
return nil, err
}
stat := getScope(c).Stat(id)
if stat == nil {
return nil, slerrors.NotFound("Stat")
}
err = db.Stats(getScope(c).ID).Delete(c.Request.Context(), *stat)
if err != nil {
return nil, err
}
return stat, nil
}))
}

1
cmd/stufflog3-server.go

@ -36,6 +36,7 @@ func main() {
api.Scopes(server.Group("/api/scopes"), db)
api.Items(server.Group("/api/scope/:scope_id/items"), db)
api.Projects(server.Group("/api/scope/:scope_id/projects"), db)
api.Stats(server.Group("/api/scope/:scope_id/stats"), db)
exitSignal := make(chan os.Signal)
signal.Notify(exitSignal, os.Interrupt, os.Kill, syscall.SIGTERM)

12
internal/database/database.go

@ -29,7 +29,7 @@ type StatRepository interface {
Find(ctx context.Context, id int) (*models.Stat, error)
List(ctx context.Context) ([]models.Stat, error)
Create(ctx context.Context, stat models.Stat) (*models.Stat, error)
Update(ctx context.Context, stat models.Stat) error
Update(ctx context.Context, stat models.Stat, update models.StatUpdate) (*models.Stat, error)
Delete(ctx context.Context, stat models.Stat) error
}
@ -55,10 +55,10 @@ type ProjectRepository interface {
Find(ctx context.Context, id int) (*models.Project, error)
List(ctx context.Context) ([]models.ProjectEntry, error)
Create(ctx context.Context, project models.Project) (*models.Project, error)
Update(ctx context.Context, project models.Project) (*models.Project, error)
Delete(ctx context.Context, project models.ProjectEntry) error
Update(ctx context.Context, project models.Project, update models.ProjectUpdate) (*models.Project, error)
Delete(ctx context.Context, project models.ProjectEntry, deleteItems bool) error
AddRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement)
UpdateRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement, update models.ProjectRequirementUpdate)
DeleteRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement, deleteItems bool)
AddRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement) (*models.Project, error)
UpdateRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement, update models.ProjectRequirementUpdate) (*models.Project, error)
DeleteRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement, deleteItems bool) (*models.Project, error)
}

6
internal/database/mysql/database.go

@ -21,8 +21,10 @@ func (d *Database) Scopes() database.ScopeRepository {
}
func (d *Database) Stats(scopeID int) database.StatRepository {
//TODO implement me
panic("implement me")
return &statsRepository{
db: d.db, q: d.q,
scopeID: scopeID,
}
}
func (d *Database) Items(scopeID int) database.ItemRepository {

294
internal/database/mysql/mysqlcore/db.go

@ -24,12 +24,27 @@ func New(db DBTX) *Queries {
func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
q := Queries{db: db}
var err error
if q.cLearItemStatProgressByStatStmt, err = db.PrepareContext(ctx, cLearItemStatProgressByStat); err != nil {
return nil, fmt.Errorf("error preparing query CLearItemStatProgressByStat: %w", err)
}
if q.clearItemProjectRequirementStmt, err = db.PrepareContext(ctx, clearItemProjectRequirement); err != nil {
return nil, fmt.Errorf("error preparing query ClearItemProjectRequirement: %w", err)
}
if q.clearItemProjectRequirementByProjectIDStmt, err = db.PrepareContext(ctx, clearItemProjectRequirementByProjectID); err != nil {
return nil, fmt.Errorf("error preparing query ClearItemProjectRequirementByProjectID: %w", err)
}
if q.clearItemStatProgressStmt, err = db.PrepareContext(ctx, clearItemStatProgress); err != nil {
return nil, fmt.Errorf("error preparing query ClearItemStatProgress: %w", err)
}
if q.deleteAllProjectRequirementStatsStmt, err = db.PrepareContext(ctx, deleteAllProjectRequirementStats); err != nil {
return nil, fmt.Errorf("error preparing query DeleteAllProjectRequirementStats: %w", err)
}
if q.deleteAllProjectRequirementStatsByStatStmt, err = db.PrepareContext(ctx, deleteAllProjectRequirementStatsByStat); err != nil {
return nil, fmt.Errorf("error preparing query DeleteAllProjectRequirementStatsByStat: %w", err)
}
if q.deleteAllProjectRequirementsStmt, err = db.PrepareContext(ctx, deleteAllProjectRequirements); err != nil {
return nil, fmt.Errorf("error preparing query DeleteAllProjectRequirements: %w", err)
}
if q.deleteAllScopeMembersStmt, err = db.PrepareContext(ctx, deleteAllScopeMembers); err != nil {
return nil, fmt.Errorf("error preparing query DeleteAllScopeMembers: %w", err)
}
@ -48,12 +63,18 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
if q.deleteProjectRequirementStmt, err = db.PrepareContext(ctx, deleteProjectRequirement); err != nil {
return nil, fmt.Errorf("error preparing query DeleteProjectRequirement: %w", err)
}
if q.deleteProjectRequirementStatStmt, err = db.PrepareContext(ctx, deleteProjectRequirementStat); err != nil {
return nil, fmt.Errorf("error preparing query DeleteProjectRequirementStat: %w", err)
}
if q.deleteScopeStmt, err = db.PrepareContext(ctx, deleteScope); err != nil {
return nil, fmt.Errorf("error preparing query DeleteScope: %w", err)
}
if q.deleteScopeMemberStmt, err = db.PrepareContext(ctx, deleteScopeMember); err != nil {
return nil, fmt.Errorf("error preparing query DeleteScopeMember: %w", err)
}
if q.deleteStatStmt, err = db.PrepareContext(ctx, deleteStat); err != nil {
return nil, fmt.Errorf("error preparing query DeleteStat: %w", err)
}
if q.getItemStmt, err = db.PrepareContext(ctx, getItem); err != nil {
return nil, fmt.Errorf("error preparing query GetItem: %w", err)
}
@ -75,6 +96,9 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
if q.getScopeWithDisplayNameStmt, err = db.PrepareContext(ctx, getScopeWithDisplayName); err != nil {
return nil, fmt.Errorf("error preparing query GetScopeWithDisplayName: %w", err)
}
if q.getStatStmt, err = db.PrepareContext(ctx, getStat); err != nil {
return nil, fmt.Errorf("error preparing query GetStat: %w", err)
}
if q.insertItemStmt, err = db.PrepareContext(ctx, insertItem); err != nil {
return nil, fmt.Errorf("error preparing query InsertItem: %w", err)
}
@ -87,6 +111,9 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
if q.insertScopeStmt, err = db.PrepareContext(ctx, insertScope); err != nil {
return nil, fmt.Errorf("error preparing query InsertScope: %w", err)
}
if q.insertStatStmt, err = db.PrepareContext(ctx, insertStat); err != nil {
return nil, fmt.Errorf("error preparing query InsertStat: %w", err)
}
if q.listItemStatProgressStmt, err = db.PrepareContext(ctx, listItemStatProgress); err != nil {
return nil, fmt.Errorf("error preparing query ListItemStatProgress: %w", err)
}
@ -141,6 +168,9 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
if q.replaceItemStatProgressStmt, err = db.PrepareContext(ctx, replaceItemStatProgress); err != nil {
return nil, fmt.Errorf("error preparing query ReplaceItemStatProgress: %w", err)
}
if q.replaceProjectRequirementStatStmt, err = db.PrepareContext(ctx, replaceProjectRequirementStat); err != nil {
return nil, fmt.Errorf("error preparing query ReplaceProjectRequirementStat: %w", err)
}
if q.updateItemStmt, err = db.PrepareContext(ctx, updateItem); err != nil {
return nil, fmt.Errorf("error preparing query UpdateItem: %w", err)
}
@ -156,21 +186,49 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
if q.updateScopeMemberStmt, err = db.PrepareContext(ctx, updateScopeMember); err != nil {
return nil, fmt.Errorf("error preparing query UpdateScopeMember: %w", err)
}
if q.updateStatStmt, err = db.PrepareContext(ctx, updateStat); err != nil {
return nil, fmt.Errorf("error preparing query UpdateStat: %w", err)
}
return &q, nil
}
func (q *Queries) Close() error {
var err error
if q.cLearItemStatProgressByStatStmt != nil {
if cerr := q.cLearItemStatProgressByStatStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing cLearItemStatProgressByStatStmt: %w", cerr)
}
}
if q.clearItemProjectRequirementStmt != nil {
if cerr := q.clearItemProjectRequirementStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing clearItemProjectRequirementStmt: %w", cerr)
}
}
if q.clearItemProjectRequirementByProjectIDStmt != nil {
if cerr := q.clearItemProjectRequirementByProjectIDStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing clearItemProjectRequirementByProjectIDStmt: %w", cerr)
}
}
if q.clearItemStatProgressStmt != nil {
if cerr := q.clearItemStatProgressStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing clearItemStatProgressStmt: %w", cerr)
}
}
if q.deleteAllProjectRequirementStatsStmt != nil {
if cerr := q.deleteAllProjectRequirementStatsStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing deleteAllProjectRequirementStatsStmt: %w", cerr)
}
}
if q.deleteAllProjectRequirementStatsByStatStmt != nil {
if cerr := q.deleteAllProjectRequirementStatsByStatStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing deleteAllProjectRequirementStatsByStatStmt: %w", cerr)
}
}
if q.deleteAllProjectRequirementsStmt != nil {
if cerr := q.deleteAllProjectRequirementsStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing deleteAllProjectRequirementsStmt: %w", cerr)
}
}
if q.deleteAllScopeMembersStmt != nil {
if cerr := q.deleteAllScopeMembersStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing deleteAllScopeMembersStmt: %w", cerr)
@ -201,6 +259,11 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing deleteProjectRequirementStmt: %w", cerr)
}
}
if q.deleteProjectRequirementStatStmt != nil {
if cerr := q.deleteProjectRequirementStatStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing deleteProjectRequirementStatStmt: %w", cerr)
}
}
if q.deleteScopeStmt != nil {
if cerr := q.deleteScopeStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing deleteScopeStmt: %w", cerr)
@ -211,6 +274,11 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing deleteScopeMemberStmt: %w", cerr)
}
}
if q.deleteStatStmt != nil {
if cerr := q.deleteStatStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing deleteStatStmt: %w", cerr)
}
}
if q.getItemStmt != nil {
if cerr := q.getItemStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing getItemStmt: %w", cerr)
@ -246,6 +314,11 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing getScopeWithDisplayNameStmt: %w", cerr)
}
}
if q.getStatStmt != nil {
if cerr := q.getStatStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing getStatStmt: %w", cerr)
}
}
if q.insertItemStmt != nil {
if cerr := q.insertItemStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing insertItemStmt: %w", cerr)
@ -266,6 +339,11 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing insertScopeStmt: %w", cerr)
}
}
if q.insertStatStmt != nil {
if cerr := q.insertStatStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing insertStatStmt: %w", cerr)
}
}
if q.listItemStatProgressStmt != nil {
if cerr := q.listItemStatProgressStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing listItemStatProgressStmt: %w", cerr)
@ -356,6 +434,11 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing replaceItemStatProgressStmt: %w", cerr)
}
}
if q.replaceProjectRequirementStatStmt != nil {
if cerr := q.replaceProjectRequirementStatStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing replaceProjectRequirementStatStmt: %w", cerr)
}
}
if q.updateItemStmt != nil {
if cerr := q.updateItemStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing updateItemStmt: %w", cerr)
@ -381,6 +464,11 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing updateScopeMemberStmt: %w", cerr)
}
}
if q.updateStatStmt != nil {
if cerr := q.updateStatStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing updateStatStmt: %w", cerr)
}
}
return err
}
@ -418,101 +506,123 @@ func (q *Queries) queryRow(ctx context.Context, stmt *sql.Stmt, query string, ar
}
type Queries struct {
db DBTX
tx *sql.Tx
clearItemProjectRequirementStmt *sql.Stmt
clearItemStatProgressStmt *sql.Stmt
deleteAllScopeMembersStmt *sql.Stmt
deleteItemStmt *sql.Stmt
deleteItemForRequirementStmt *sql.Stmt
deleteItemStatProgressStmt *sql.Stmt
deleteProjectStmt *sql.Stmt
deleteProjectRequirementStmt *sql.Stmt
deleteScopeStmt *sql.Stmt
deleteScopeMemberStmt *sql.Stmt
getItemStmt *sql.Stmt
getItemStatProgressBetweenStmt *sql.Stmt
getProjectStmt *sql.Stmt
getProjectRequirementStmt *sql.Stmt
getScopeStmt *sql.Stmt
getScopeDisplayNameStmt *sql.Stmt
getScopeWithDisplayNameStmt *sql.Stmt
insertItemStmt *sql.Stmt
insertProjectStmt *sql.Stmt
insertProjectRequirementStmt *sql.Stmt
insertScopeStmt *sql.Stmt
listItemStatProgressStmt *sql.Stmt
listItemStatProgressMultiStmt *sql.Stmt
listItemsAcquiredBetweenStmt *sql.Stmt
listItemsByProjectStmt *sql.Stmt
listItemsCreatedBetweenStmt *sql.Stmt
listItemsCreatedBetweenNoScopeStmt *sql.Stmt
listItemsLooseBetweenStmt *sql.Stmt
listItemsLooseBetweenNoScopeStmt *sql.Stmt
listItemsScheduledBetweenStmt *sql.Stmt
listItemsScheduledBetweenNoScopeStmt *sql.Stmt
listProjectEntriesStmt *sql.Stmt
listProjectRequirementStatsStmt *sql.Stmt
listProjectRequirementsByProjectIDStmt *sql.Stmt
listScopeMembersStmt *sql.Stmt
listScopesStmt *sql.Stmt
listScopesByUserStmt *sql.Stmt
listStatsStmt *sql.Stmt
replaceItemStatProgressStmt *sql.Stmt
updateItemStmt *sql.Stmt
updateProjectStmt *sql.Stmt
updateProjectRequirementStmt *sql.Stmt
updateScopeStmt *sql.Stmt
updateScopeMemberStmt *sql.Stmt
db DBTX
tx *sql.Tx
cLearItemStatProgressByStatStmt *sql.Stmt
clearItemProjectRequirementStmt *sql.Stmt
clearItemProjectRequirementByProjectIDStmt *sql.Stmt
clearItemStatProgressStmt *sql.Stmt
deleteAllProjectRequirementStatsStmt *sql.Stmt
deleteAllProjectRequirementStatsByStatStmt *sql.Stmt
deleteAllProjectRequirementsStmt *sql.Stmt
deleteAllScopeMembersStmt *sql.Stmt
deleteItemStmt *sql.Stmt
deleteItemForRequirementStmt *sql.Stmt
deleteItemStatProgressStmt *sql.Stmt
deleteProjectStmt *sql.Stmt
deleteProjectRequirementStmt *sql.Stmt
deleteProjectRequirementStatStmt *sql.Stmt
deleteScopeStmt *sql.Stmt
deleteScopeMemberStmt *sql.Stmt
deleteStatStmt *sql.Stmt
getItemStmt *sql.Stmt
getItemStatProgressBetweenStmt *sql.Stmt
getProjectStmt *sql.Stmt
getProjectRequirementStmt *sql.Stmt
getScopeStmt *sql.Stmt
getScopeDisplayNameStmt *sql.Stmt
getScopeWithDisplayNameStmt *sql.Stmt
getStatStmt *sql.Stmt
insertItemStmt *sql.Stmt
insertProjectStmt *sql.Stmt
insertProjectRequirementStmt *sql.Stmt
insertScopeStmt *sql.Stmt
insertStatStmt *sql.Stmt
listItemStatProgressStmt *sql.Stmt
listItemStatProgressMultiStmt *sql.Stmt
listItemsAcquiredBetweenStmt *sql.Stmt
listItemsByProjectStmt *sql.Stmt
listItemsCreatedBetweenStmt *sql.Stmt
listItemsCreatedBetweenNoScopeStmt *sql.Stmt
listItemsLooseBetweenStmt *sql.Stmt
listItemsLooseBetweenNoScopeStmt *sql.Stmt
listItemsScheduledBetweenStmt *sql.Stmt
listItemsScheduledBetweenNoScopeStmt *sql.Stmt
listProjectEntriesStmt *sql.Stmt
listProjectRequirementStatsStmt *sql.Stmt
listProjectRequirementsByProjectIDStmt *sql.Stmt
listScopeMembersStmt *sql.Stmt
listScopesStmt *sql.Stmt
listScopesByUserStmt *sql.Stmt
listStatsStmt *sql.Stmt
replaceItemStatProgressStmt *sql.Stmt
replaceProjectRequirementStatStmt *sql.Stmt
updateItemStmt *sql.Stmt
updateProjectStmt *sql.Stmt
updateProjectRequirementStmt *sql.Stmt
updateScopeStmt *sql.Stmt
updateScopeMemberStmt *sql.Stmt
updateStatStmt *sql.Stmt
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
tx: tx,
clearItemProjectRequirementStmt: q.clearItemProjectRequirementStmt,
clearItemStatProgressStmt: q.clearItemStatProgressStmt,
deleteAllScopeMembersStmt: q.deleteAllScopeMembersStmt,
deleteItemStmt: q.deleteItemStmt,
deleteItemForRequirementStmt: q.deleteItemForRequirementStmt,
deleteItemStatProgressStmt: q.deleteItemStatProgressStmt,
deleteProjectStmt: q.deleteProjectStmt,
deleteProjectRequirementStmt: q.deleteProjectRequirementStmt,
deleteScopeStmt: q.deleteScopeStmt,
deleteScopeMemberStmt: q.deleteScopeMemberStmt,
getItemStmt: q.getItemStmt,
getItemStatProgressBetweenStmt: q.getItemStatProgressBetweenStmt,
getProjectStmt: q.getProjectStmt,
getProjectRequirementStmt: q.getProjectRequirementStmt,
getScopeStmt: q.getScopeStmt,
getScopeDisplayNameStmt: q.getScopeDisplayNameStmt,
getScopeWithDisplayNameStmt: q.getScopeWithDisplayNameStmt,
insertItemStmt: q.insertItemStmt,
insertProjectStmt: q.insertProjectStmt,
insertProjectRequirementStmt: q.insertProjectRequirementStmt,
insertScopeStmt: q.insertScopeStmt,
listItemStatProgressStmt: q.listItemStatProgressStmt,
listItemStatProgressMultiStmt: q.listItemStatProgressMultiStmt,
listItemsAcquiredBetweenStmt: q.listItemsAcquiredBetweenStmt,
listItemsByProjectStmt: q.listItemsByProjectStmt,
listItemsCreatedBetweenStmt: q.listItemsCreatedBetweenStmt,
listItemsCreatedBetweenNoScopeStmt: q.listItemsCreatedBetweenNoScopeStmt,
listItemsLooseBetweenStmt: q.listItemsLooseBetweenStmt,
listItemsLooseBetweenNoScopeStmt: q.listItemsLooseBetweenNoScopeStmt,
listItemsScheduledBetweenStmt: q.listItemsScheduledBetweenStmt,
listItemsScheduledBetweenNoScopeStmt: q.listItemsScheduledBetweenNoScopeStmt,
listProjectEntriesStmt: q.listProjectEntriesStmt,
listProjectRequirementStatsStmt: q.listProjectRequirementStatsStmt,
listProjectRequirementsByProjectIDStmt: q.listProjectRequirementsByProjectIDStmt,
listScopeMembersStmt: q.listScopeMembersStmt,
listScopesStmt: q.listScopesStmt,
listScopesByUserStmt: q.listScopesByUserStmt,
listStatsStmt: q.listStatsStmt,
replaceItemStatProgressStmt: q.replaceItemStatProgressStmt,
updateItemStmt: q.updateItemStmt,
updateProjectStmt: q.updateProjectStmt,
updateProjectRequirementStmt: q.updateProjectRequirementStmt,
updateScopeStmt: q.updateScopeStmt,
updateScopeMemberStmt: q.updateScopeMemberStmt,
db: tx,
tx: tx,
cLearItemStatProgressByStatStmt: q.cLearItemStatProgressByStatStmt,
clearItemProjectRequirementStmt: q.clearItemProjectRequirementStmt,
clearItemProjectRequirementByProjectIDStmt: q.clearItemProjectRequirementByProjectIDStmt,
clearItemStatProgressStmt: q.clearItemStatProgressStmt,
deleteAllProjectRequirementStatsStmt: q.deleteAllProjectRequirementStatsStmt,
deleteAllProjectRequirementStatsByStatStmt: q.deleteAllProjectRequirementStatsByStatStmt,
deleteAllProjectRequirementsStmt: q.deleteAllProjectRequirementsStmt,
deleteAllScopeMembersStmt: q.deleteAllScopeMembersStmt,
deleteItemStmt: q.deleteItemStmt,
deleteItemForRequirementStmt: q.deleteItemForRequirementStmt,
deleteItemStatProgressStmt: q.deleteItemStatProgressStmt,
deleteProjectStmt: q.deleteProjectStmt,
deleteProjectRequirementStmt: q.deleteProjectRequirementStmt,
deleteProjectRequirementStatStmt: q.deleteProjectRequirementStatStmt,
deleteScopeStmt: q.deleteScopeStmt,
deleteScopeMemberStmt: q.deleteScopeMemberStmt,
deleteStatStmt: q.deleteStatStmt,
getItemStmt: q.getItemStmt,
getItemStatProgressBetweenStmt: q.getItemStatProgressBetweenStmt,
getProjectStmt: q.getProjectStmt,
getProjectRequirementStmt: q.getProjectRequirementStmt,
getScopeStmt: q.getScopeStmt,
getScopeDisplayNameStmt: q.getScopeDisplayNameStmt,
getScopeWithDisplayNameStmt: q.getScopeWithDisplayNameStmt,
getStatStmt: q.getStatStmt,
insertItemStmt: q.insertItemStmt,
insertProjectStmt: q.insertProjectStmt,
insertProjectRequirementStmt: q.insertProjectRequirementStmt,
insertScopeStmt: q.insertScopeStmt,
insertStatStmt: q.insertStatStmt,
listItemStatProgressStmt: q.listItemStatProgressStmt,
listItemStatProgressMultiStmt: q.listItemStatProgressMultiStmt,
listItemsAcquiredBetweenStmt: q.listItemsAcquiredBetweenStmt,
listItemsByProjectStmt: q.listItemsByProjectStmt,
listItemsCreatedBetweenStmt: q.listItemsCreatedBetweenStmt,
listItemsCreatedBetweenNoScopeStmt: q.listItemsCreatedBetweenNoScopeStmt,
listItemsLooseBetweenStmt: q.listItemsLooseBetweenStmt,
listItemsLooseBetweenNoScopeStmt: q.listItemsLooseBetweenNoScopeStmt,
listItemsScheduledBetweenStmt: q.listItemsScheduledBetweenStmt,
listItemsScheduledBetweenNoScopeStmt: q.listItemsScheduledBetweenNoScopeStmt,
listProjectEntriesStmt: q.listProjectEntriesStmt,
listProjectRequirementStatsStmt: q.listProjectRequirementStatsStmt,
listProjectRequirementsByProjectIDStmt: q.listProjectRequirementsByProjectIDStmt,
listScopeMembersStmt: q.listScopeMembersStmt,
listScopesStmt: q.listScopesStmt,
listScopesByUserStmt: q.listScopesByUserStmt,
listStatsStmt: q.listStatsStmt,
replaceItemStatProgressStmt: q.replaceItemStatProgressStmt,
replaceProjectRequirementStatStmt: q.replaceProjectRequirementStatStmt,
updateItemStmt: q.updateItemStmt,
updateProjectStmt: q.updateProjectStmt,
updateProjectRequirementStmt: q.updateProjectRequirementStmt,
updateScopeStmt: q.updateScopeStmt,
updateScopeMemberStmt: q.updateScopeMemberStmt,
updateStatStmt: q.updateStatStmt,
}
}

21
internal/database/mysql/mysqlcore/item.sql.go

@ -13,6 +13,15 @@ import (
"git.aiterp.net/stufflog3/stufflog3-api/internal/sqltypes"
)
const cLearItemStatProgressByStat = `-- name: CLearItemStatProgressByStat :exec
DELETE FROM item_stat_progress WHERE stat_id = ?
`
func (q *Queries) CLearItemStatProgressByStat(ctx context.Context, statID int) error {
_, err := q.exec(ctx, q.cLearItemStatProgressByStatStmt, cLearItemStatProgressByStat, statID)
return err
}
const clearItemProjectRequirement = `-- name: ClearItemProjectRequirement :exec
UPDATE item SET project_requirement_id = NULL WHERE project_requirement_id = ?
`
@ -22,6 +31,18 @@ func (q *Queries) ClearItemProjectRequirement(ctx context.Context, projectRequir
return err
}
const clearItemProjectRequirementByProjectID = `-- name: ClearItemProjectRequirementByProjectID :exec
UPDATE item i
LEFT JOIN project_requirement pr ON pr.id = i.project_requirement_id
SET i.project_requirement_id = NULL
WHERE pr.project_id = ?
`
func (q *Queries) ClearItemProjectRequirementByProjectID(ctx context.Context, projectID int) error {
_, err := q.exec(ctx, q.clearItemProjectRequirementByProjectIDStmt, clearItemProjectRequirementByProjectID, projectID)
return err
}
const clearItemStatProgress = `-- name: ClearItemStatProgress :exec
DELETE FROM item_stat_progress WHERE item_id = ?
`

57
internal/database/mysql/mysqlcore/project.sql.go

@ -11,6 +11,33 @@ import (
"time"
)
const deleteAllProjectRequirementStats = `-- name: DeleteAllProjectRequirementStats :exec
DELETE FROM project_requirement_stat WHERE project_requirement_id = ?
`
func (q *Queries) DeleteAllProjectRequirementStats(ctx context.Context, projectRequirementID int) error {
_, err := q.exec(ctx, q.deleteAllProjectRequirementStatsStmt, deleteAllProjectRequirementStats, projectRequirementID)
return err
}
const deleteAllProjectRequirementStatsByStat = `-- name: DeleteAllProjectRequirementStatsByStat :exec
DELETE FROM project_requirement_stat WHERE stat_id = ?
`
func (q *Queries) DeleteAllProjectRequirementStatsByStat(ctx context.Context, statID int) error {
_, err := q.exec(ctx, q.deleteAllProjectRequirementStatsByStatStmt, deleteAllProjectRequirementStatsByStat, statID)
return err
}
const deleteAllProjectRequirements = `-- name: DeleteAllProjectRequirements :exec
DELETE FROM project_requirement WHERE project_id = ?
`
func (q *Queries) DeleteAllProjectRequirements(ctx context.Context, projectID int) error {
_, err := q.exec(ctx, q.deleteAllProjectRequirementsStmt, deleteAllProjectRequirements, projectID)
return err
}
const deleteProject = `-- name: DeleteProject :exec
DELETE FROM project WHERE id = ?
`
@ -29,6 +56,20 @@ func (q *Queries) DeleteProjectRequirement(ctx context.Context, id int) error {
return err
}
const deleteProjectRequirementStat = `-- name: DeleteProjectRequirementStat :exec
DELETE FROM project_requirement_stat WHERE project_requirement_id = ? AND stat_id = ?
`
type DeleteProjectRequirementStatParams struct {
ProjectRequirementID int
StatID int
}
func (q *Queries) DeleteProjectRequirementStat(ctx context.Context, arg DeleteProjectRequirementStatParams) error {
_, err := q.exec(ctx, q.deleteProjectRequirementStatStmt, deleteProjectRequirementStat, arg.ProjectRequirementID, arg.StatID)
return err
}
const getProject = `-- name: GetProject :one
SELECT id, scope_id, author_id, name, status, description, created_time FROM project WHERE id = ?
`
@ -232,6 +273,22 @@ func (q *Queries) ListProjectRequirementsByProjectID(ctx context.Context, projec
return items, nil
}
const replaceProjectRequirementStat = `-- name: ReplaceProjectRequirementStat :exec
REPLACE INTO project_requirement_stat (project_requirement_id, stat_id, required)
VALUES (?, ?, ?)
`
type ReplaceProjectRequirementStatParams struct {
ProjectRequirementID int
StatID int
Required int
}
func (q *Queries) ReplaceProjectRequirementStat(ctx context.Context, arg ReplaceProjectRequirementStatParams) error {
_, err := q.exec(ctx, q.replaceProjectRequirementStatStmt, replaceProjectRequirementStat, arg.ProjectRequirementID, arg.StatID, arg.Required)
return err
}
const updateProject = `-- name: UpdateProject :exec
UPDATE project
SET name = ?,

99
internal/database/mysql/mysqlcore/stats.sql.go

@ -7,10 +7,79 @@ package mysqlcore
import (
"context"
"database/sql"
"git.aiterp.net/stufflog3/stufflog3-api/internal/sqltypes"
)
const deleteStat = `-- name: DeleteStat :exec
DELETE FROM stat WHERE id = ? AND scope_id = ?
`
type DeleteStatParams struct {
ID int
ScopeID int
}
func (q *Queries) DeleteStat(ctx context.Context, arg DeleteStatParams) error {
_, err := q.exec(ctx, q.deleteStatStmt, deleteStat, arg.ID, arg.ScopeID)
return err
}
const getStat = `-- name: GetStat :one
SELECT id, name, description, weight, allowed_amounts FROM stat
WHERE scope_id = ? AND id = ?
`
type GetStatParams struct {
ScopeID int
ID int
}
type GetStatRow struct {
ID int
Name string
Description string
Weight float64
AllowedAmounts sqltypes.NullRawMessage
}
func (q *Queries) GetStat(ctx context.Context, arg GetStatParams) (GetStatRow, error) {
row := q.queryRow(ctx, q.getStatStmt, getStat, arg.ScopeID, arg.ID)
var i GetStatRow
err := row.Scan(
&i.ID,
&i.Name,
&i.Description,
&i.Weight,
&i.AllowedAmounts,
)
return i, err
}
const insertStat = `-- name: InsertStat :execresult
INSERT INTO stat (scope_id, name, description, weight, allowed_amounts)
VALUES (?, ?, ?, ?, ?)
`
type InsertStatParams struct {
ScopeID int
Name string
Description string
Weight float64
AllowedAmounts sqltypes.NullRawMessage
}
func (q *Queries) InsertStat(ctx context.Context, arg InsertStatParams) (sql.Result, error) {
return q.exec(ctx, q.insertStatStmt, insertStat,
arg.ScopeID,
arg.Name,
arg.Description,
arg.Weight,
arg.AllowedAmounts,
)
}
const listStats = `-- name: ListStats :many
SELECT id, name, description, weight, allowed_amounts FROM stat
WHERE scope_id = ?
@ -52,3 +121,33 @@ func (q *Queries) ListStats(ctx context.Context, scopeID int) ([]ListStatsRow, e
}
return items, nil
}
const updateStat = `-- name: UpdateStat :exec
UPDATE stat SET
name = ?,
description = ?,
weight = ?,
allowed_amounts = ?
WHERE id = ? AND scope_id = ?
`
type UpdateStatParams struct {
Name string
Description string
Weight float64
AllowedAmounts sqltypes.NullRawMessage
ID int
ScopeID int
}
func (q *Queries) UpdateStat(ctx context.Context, arg UpdateStatParams) error {
_, err := q.exec(ctx, q.updateStatStmt, updateStat,
arg.Name,
arg.Description,
arg.Weight,
arg.AllowedAmounts,
arg.ID,
arg.ScopeID,
)
return err
}

190
internal/database/mysql/project.go

@ -154,27 +154,187 @@ func (r *projectRepository) Create(ctx context.Context, project models.Project)
return r.Find(ctx, int(id))
}
func (r *projectRepository) Update(ctx context.Context, project models.Project) (*models.Project, error) {
//TODO implement me
panic("implement me")
func (r *projectRepository) Update(ctx context.Context, project models.Project, update models.ProjectUpdate) (*models.Project, error) {
project.Update(update)
err := r.q.UpdateProject(ctx, mysqlcore.UpdateProjectParams{
Name: project.Name,
Status: int(project.Status),
Description: project.Description,
ID: project.ID,
})
if err != nil {
return nil, err
}
return r.Find(ctx, project.ID)
}
func (r *projectRepository) Delete(ctx context.Context, project models.ProjectEntry) error {
//TODO implement me
panic("implement me")
func (r *projectRepository) Delete(ctx context.Context, project models.ProjectEntry, deleteItems bool) error {
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
q := r.q.WithTx(tx)
err = q.DeleteProject(ctx, project.ID)
if err != nil {
return err
}
if deleteItems {
reqs, err := q.ListProjectRequirementsByProjectID(ctx, project.ID)
if err != nil {
return err
}
for _, req := range reqs {
err = q.DeleteItemForRequirement(ctx, sql.NullInt32{Valid: true, Int32: int32(req.ID)})
if err != nil {
return err
}
err = q.DeleteAllProjectRequirementStats(ctx, req.ID)
if err != nil {
return err
}
}
} else {
err = q.ClearItemProjectRequirementByProjectID(ctx, project.ID)
if err != nil {
return err
}
}
return tx.Commit()
}
func (r *projectRepository) AddRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement) {
//TODO implement me
panic("implement me")
func (r *projectRepository) AddRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement) (*models.Project, error) {
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return nil, err
}
defer tx.Rollback()
q := r.q.WithTx(tx)
res, err := q.InsertProjectRequirement(ctx, mysqlcore.InsertProjectRequirementParams{
ScopeID: r.scopeID,
ProjectID: project.ID,
Name: requirement.Name,
Status: int(requirement.Status),
Description: requirement.Description,
})
if err != nil {
return nil, err
}
id, err := res.LastInsertId()
if err != nil {
return nil, err
}
for _, stat := range requirement.Stats {
err = q.ReplaceProjectRequirementStat(ctx, mysqlcore.ReplaceProjectRequirementStatParams{
ProjectRequirementID: int(id),
StatID: stat.ID,
Required: stat.Required,
})
if err != nil {
return nil, err
}
}
err = tx.Commit()
if err != nil {
return nil, err
}
return r.Find(ctx, project.ID)
}
func (r *projectRepository) UpdateRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement, update models.ProjectRequirementUpdate) {
//TODO implement me
panic("implement me")
func (r *projectRepository) UpdateRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement, update models.ProjectRequirementUpdate) (*models.Project, error) {
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return nil, err
}
defer tx.Rollback()
q := r.q.WithTx(tx)
requirement.Update(update)
err = q.UpdateProjectRequirement(ctx, mysqlcore.UpdateProjectRequirementParams{
Name: requirement.Name,
Status: int(requirement.Status),
Description: requirement.Description,
ID: requirement.ID,
})
if err != nil {
return nil, err
}
for _, stat := range requirement.Stats {
if stat.Required < 0 {
err = q.DeleteProjectRequirementStat(ctx, mysqlcore.DeleteProjectRequirementStatParams{
ProjectRequirementID: requirement.ID,
StatID: stat.ID,
})
} else {
err = q.ReplaceProjectRequirementStat(ctx, mysqlcore.ReplaceProjectRequirementStatParams{
ProjectRequirementID: requirement.ID,
StatID: stat.ID,
Required: stat.Required,
})
}
if err != nil {
return nil, err
}
}
err = tx.Commit()
if err != nil {
return nil, err
}
return r.Find(ctx, project.ID)
}
func (r *projectRepository) DeleteRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement, deleteItems bool) {
//TODO implement me
panic("implement me")
func (r *projectRepository) DeleteRequirement(ctx context.Context, project models.ProjectEntry, requirement models.ProjectRequirement, deleteItems bool) (*models.Project, error) {
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return nil, err
}
defer tx.Rollback()
q := r.q.WithTx(tx)
err = q.DeleteProjectRequirement(ctx, requirement.ID)
if err != nil {
if err == sql.ErrNoRows {
return nil, slerrors.NotFound("Project requirement")
}
return nil, err
}
if deleteItems {
err = q.DeleteItemForRequirement(ctx, sql.NullInt32{Valid: true, Int32: int32(requirement.ID)})
if err != nil {
return nil, err
}
} else {
err = q.ClearItemProjectRequirement(ctx, sql.NullInt32{Valid: true, Int32: int32(requirement.ID)})
if err != nil {
return nil, err
}
err = q.DeleteAllProjectRequirementStats(ctx, requirement.ID)
if err != nil {
return nil, err
}
}
err = tx.Commit()
if err != nil {
return nil, err
}
return r.Find(ctx, project.ID)
}

9
internal/database/mysql/queries/item.sql

@ -105,6 +105,12 @@ DELETE FROM item WHERE project_requirement_id = ?;
-- name: ClearItemProjectRequirement :exec
UPDATE item SET project_requirement_id = NULL WHERE project_requirement_id = ?;
-- name: ClearItemProjectRequirementByProjectID :exec
UPDATE item i
LEFT JOIN project_requirement pr ON pr.id = i.project_requirement_id
SET i.project_requirement_id = NULL
WHERE pr.project_id = ?;
-- name: ReplaceItemStatProgress :exec
REPLACE INTO item_stat_progress (item_id, stat_id, acquired, required)
VALUES (?, ?, ?, ?);
@ -114,3 +120,6 @@ DELETE FROM item_stat_progress WHERE item_id = ? AND stat_id = ?;
-- name: ClearItemStatProgress :exec
DELETE FROM item_stat_progress WHERE item_id = ?;
-- name: CLearItemStatProgressByStat :exec
DELETE FROM item_stat_progress WHERE stat_id = ?;

15
internal/database/mysql/queries/project.sql

@ -45,4 +45,19 @@ WHERE id = ?;
-- name: DeleteProjectRequirement :exec
DELETE FROM project_requirement WHERE id = ?;
-- name: DeleteAllProjectRequirements :exec
DELETE FROM project_requirement WHERE project_id = ?;
-- name: ReplaceProjectRequirementStat :exec
REPLACE INTO project_requirement_stat (project_requirement_id, stat_id, required)
VALUES (?, ?, ?);
-- name: DeleteProjectRequirementStat :exec
DELETE FROM project_requirement_stat WHERE project_requirement_id = ? AND stat_id = ?;
-- name: DeleteAllProjectRequirementStats :exec
DELETE FROM project_requirement_stat WHERE project_requirement_id = ?;
-- name: DeleteAllProjectRequirementStatsByStat :exec
DELETE FROM project_requirement_stat WHERE stat_id = ?;

0
internal/database/mysql/queries/sprint.sql

19
internal/database/mysql/queries/stats.sql

@ -1,3 +1,22 @@
-- name: ListStats :many
SELECT id, name, description, weight, allowed_amounts FROM stat
WHERE scope_id = ?;
-- name: GetStat :one
SELECT id, name, description, weight, allowed_amounts FROM stat
WHERE scope_id = ? AND id = ?;
-- name: InsertStat :execresult
INSERT INTO stat (scope_id, name, description, weight, allowed_amounts)
VALUES (?, ?, ?, ?, ?);
-- name: UpdateStat :exec
UPDATE stat SET
name = ?,
description = ?,
weight = ?,
allowed_amounts = ?
WHERE id = ? AND scope_id = ?;
-- name: DeleteStat :exec
DELETE FROM stat WHERE id = ? AND scope_id = ?;

144
internal/database/mysql/stats.go

@ -0,0 +1,144 @@
package mysql
import (
"context"
"database/sql"
"encoding/json"
"git.aiterp.net/stufflog3/stufflog3-api/internal/database/mysql/mysqlcore"
"git.aiterp.net/stufflog3/stufflog3-api/internal/models"
"git.aiterp.net/stufflog3/stufflog3-api/internal/slerrors"
"git.aiterp.net/stufflog3/stufflog3-api/internal/sqltypes"
)
type statsRepository struct {
db *sql.DB
q *mysqlcore.Queries
scopeID int
}
func (r *statsRepository) Find(ctx context.Context, id int) (*models.Stat, error) {
row, err := r.q.GetStat(ctx, mysqlcore.GetStatParams{ScopeID: r.scopeID, ID: id})
if err != nil {
if err == sql.ErrNoRows {
return nil, slerrors.NotFound("Stat")
}
return nil, err
}
return r.rowToStat(row), nil
}
func (r *statsRepository) List(ctx context.Context) ([]models.Stat, error) {
rows, err := r.q.ListStats(ctx, r.scopeID)
if err != nil && err != sql.ErrNoRows {
return nil, err
}
stats := make([]models.Stat, 0, len(rows))
for _, row := range rows {
stats = append(stats, *r.rowToStat(mysqlcore.GetStatRow(row)))
}
return stats, nil
}
func (r *statsRepository) Create(ctx context.Context, stat models.Stat) (*models.Stat, error) {
allowedAmounts := sqltypes.NullRawMessage{}
if stat.AllowedAmounts != nil && len(stat.AllowedAmounts) > 0 {
allowedAmounts.Valid = true
allowedAmounts.RawMessage, _ = json.Marshal(stat.AllowedAmounts)
}
res, err := r.q.InsertStat(ctx, mysqlcore.InsertStatParams{
ScopeID: r.scopeID,
Name: stat.Name,
Description: stat.Description,
Weight: stat.Weight,
AllowedAmounts: allowedAmounts,
})
if err != nil {
return nil, err
}
id, err := res.LastInsertId()
if err != nil {
return nil, err
}
return r.Find(ctx, int(id))
}
func (r *statsRepository) Update(ctx context.Context, stat models.Stat, update models.StatUpdate) (*models.Stat, error) {
stat.Update(update)
allowedAmounts := sqltypes.NullRawMessage{}
if stat.AllowedAmounts != nil && len(stat.AllowedAmounts) > 0 {
allowedAmounts.Valid = true
allowedAmounts.RawMessage, _ = json.Marshal(stat.AllowedAmounts)
}
err := r.q.UpdateStat(ctx, mysqlcore.UpdateStatParams{
Name: stat.Name,
Description: stat.Description,
Weight: stat.Weight,
AllowedAmounts: allowedAmounts,
ID: stat.ID,
ScopeID: r.scopeID,
})
if err != nil {
return nil, err
}
return &stat, nil
}
func (r *statsRepository) Delete(ctx context.Context, stat models.Stat) error {
tx, err := r.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
q := r.q.WithTx(tx)
err = q.DeleteStat(ctx, mysqlcore.DeleteStatParams{ScopeID: r.scopeID, ID: stat.ID})
if err == sql.ErrNoRows {
return slerrors.NotFound("Stat")
}
if err != nil {
return err
}
err = q.CLearItemStatProgressByStat(ctx, stat.ID)
if err != nil {
return err
}
err = q.DeleteAllProjectRequirementStatsByStat(ctx, stat.ID)
if err != nil {
return err
}
// TODO: delete from Sprints
return tx.Commit()
}
func (r *statsRepository) rowToStat(row mysqlcore.GetStatRow) *models.Stat {
stat := models.Stat{
StatEntry: models.StatEntry{
ID: row.ID,
Name: row.Name,
Weight: row.Weight,
},
Description: row.Description,
AllowedAmounts: nil,
}
if row.AllowedAmounts.Valid {
stat.AllowedAmounts = make(map[string]int)
_ = json.Unmarshal(row.AllowedAmounts.RawMessage, &stat)
if len(stat.AllowedAmounts) == 0 {
stat.AllowedAmounts = nil
}
}
return &stat
}

32
internal/models/project.go

@ -64,3 +64,35 @@ type Project struct {
Description string `json:"description"`
Requirements []ProjectRequirement `json:"requirements"`
}
func (project *Project) Update(update ProjectUpdate) {
if update.OwnerID != nil {
project.OwnerID = *update.OwnerID
}
if update.Name != nil {
project.Name = *update.Name
}
if update.Description != nil {
project.Description = *update.Description
}
if update.Status != nil {
project.Status = *update.Status
}
}
func (project *Project) Requirement(id int) *ProjectRequirement {
for i, requirement := range project.Requirements {
if requirement.ID == id {
return &project.Requirements[i]
}
}
return nil
}
type ProjectUpdate struct {
Name *string `json:"name,omitempty"`
OwnerID *string `json:"ownerId,omitempty"`
Status *Status `json:"status,omitempty"`
Description *string `json:"description,omitempty"`
}

32
internal/models/stat.go

@ -20,6 +20,38 @@ type Stat struct {
AllowedAmounts map[string]int `json:"allowedAmounts"`
}
func (stat *Stat) Update(update StatUpdate) {
if update.Name != nil {
stat.Name = *update.Name
}
if update.Description != nil {
stat.Description = *update.Description
}
if update.Weight != nil {
stat.Weight = *update.Weight
}
for key, value := range update.AllowedAmounts {
if stat.AllowedAmounts == nil {
stat.AllowedAmounts = make(map[string]int, len(update.AllowedAmounts))
}
if value <= 0 {
delete(stat.AllowedAmounts, key)
} else {
stat.AllowedAmounts[key] = value
}
}
if len(stat.AllowedAmounts) == 0 {
stat.AllowedAmounts = nil
}
}
type StatUpdate struct {
Name *string `json:"name"`
Description *string `json:"description"`
Weight *float64 `json:"weight"`
AllowedAmounts map[string]int `json:"allowedAmounts"`
}
func (stat *Stat) AllowsAmount(amount int) bool {
if stat == nil {
return false

2
internal/sqltypes/nullrawmessage.go

@ -32,5 +32,5 @@ func (n NullRawMessage) Value() (driver.Value, error) {
return nil, nil
}
return n.RawMessage, nil
return []byte(n.RawMessage), nil
}
Loading…
Cancel
Save