Browse Source

added remaining project endpoints.

master
Gisle Aune 2 years ago
parent
commit
d8cbc78b6a
  1. 8
      entities/project.go
  2. 142
      ports/httpapi/projects.go
  3. 21
      usecases/projects/result.go
  4. 198
      usecases/projects/service.go
  5. 20
      usecases/scopes/result.go

8
entities/project.go

@ -39,14 +39,14 @@ type Requirement struct {
Status models.Status `json:"status"`
}
func (requirement Requirement) Update(update models.RequirementUpdate) {
if update.Name != nil {
func (requirement *Requirement) Update(update models.RequirementUpdate) {
if update.Name != nil && *update.Name != "" {
requirement.Name = *update.Name
}
if update.Description != nil {
if update.Description != nil && *update.Description != "" {
requirement.Description = *update.Description
}
if update.Status != nil {
if update.Status != nil && update.Status.Valid() {
requirement.Status = *update.Status
}
}

142
ports/httpapi/projects.go

@ -2,13 +2,13 @@ package httpapi
import (
"git.aiterp.net/stufflog3/stufflog3/entities"
"git.aiterp.net/stufflog3/stufflog3/internal/genutils"
"git.aiterp.net/stufflog3/stufflog3/models"
"git.aiterp.net/stufflog3/stufflog3/usecases/projects"
"github.com/gin-gonic/gin"
)
func Projects(g *gin.RouterGroup, service *projects.Service) {
g.GET("", handler("projects", func(c *gin.Context) (interface{}, error) {
return service.List(c.Request.Context())
}))
@ -50,27 +50,153 @@ func Projects(g *gin.RouterGroup, service *projects.Service) {
}))
g.GET("/:project_id/requirements/:requirement_id", handler("requirements", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "project_id")
projectID, err := reqInt(c, "project_id")
if err != nil {
return nil, err
}
reqID, err := reqInt(c, "requirement_id")
requirementID, err := reqInt(c, "requirement_id")
if err != nil {
return nil, err
}
project, err := service.Find(c.Request.Context(), id)
project, err := service.Find(c.Request.Context(), projectID)
if err != nil {
return nil, err
}
req := genutils.Find(project.Requirements, func(r projects.RequirementResult) bool {
return r.ID == reqID
})
if err == nil {
req := project.Requirement(requirementID)
if req == nil {
return nil, models.NotFoundError("Requirement")
}
return req, nil
}))
g.POST("/:project_id/requirements", handler("requirement", func(c *gin.Context) (interface{}, error) {
input := struct {
entities.Requirement
Stats []entities.RequirementStat `json:"stats"`
}{}
err := c.BindJSON(&input)
if err != nil {
return nil, models.BadInputError{
Object: "Requirement",
Problem: "Invalid JSON: " + err.Error(),
}
}
id, err := reqInt(c, "project_id")
if err != nil {
return nil, err
}
return service.CreateRequirement(c.Request.Context(), id, input.Requirement, input.Stats)
}))
g.PUT("/:project_id/requirements/:requirement_id", handler("requirement", func(c *gin.Context) (interface{}, error) {
input := models.RequirementUpdate{}
err := c.BindJSON(&input)
if err != nil {
return nil, models.BadInputError{
Object: "RequirementUpdate",
Problem: "Invalid JSON: " + err.Error(),
}
}
projectID, err := reqInt(c, "project_id")
if err != nil {
return nil, err
}
requirementID, err := reqInt(c, "requirement_id")
if err != nil {
return nil, err
}
return service.UpdateRequirement(c.Request.Context(), projectID, requirementID, input)
}))
g.PUT("/:project_id/requirements/:requirement_id/stats/:stat_id", handler("requirement", func(c *gin.Context) (interface{}, error) {
input := entities.RequirementStat{}
err := c.BindJSON(&input)
if err != nil {
return nil, models.BadInputError{
Object: "RequirementStat",
Problem: "Invalid JSON: " + err.Error(),
}
}
projectID, err := reqInt(c, "project_id")
if err != nil {
return nil, err
}
requirementID, err := reqInt(c, "requirement_id")
if err != nil {
return nil, err
}
statID, err := reqInt(c, "stat_id")
if err != nil {
return nil, err
}
input.StatID = statID
return service.UpsertRequirementStat(c.Request.Context(), projectID, requirementID, input)
}))
g.DELETE("/:project_id/requirements/:requirement_id/stats/:stat_id", handler("requirement", func(c *gin.Context) (interface{}, error) {
projectID, err := reqInt(c, "project_id")
if err != nil {
return nil, err
}
requirementID, err := reqInt(c, "requirement_id")
if err != nil {
return nil, err
}
statID, err := reqInt(c, "stat_id")
if err != nil {
return nil, err
}
return service.DeleteRequirementStat(c.Request.Context(), projectID, requirementID, statID)
}))
g.DELETE("/:project_id/requirements/:requirement_id", handler("requirement", func(c *gin.Context) (interface{}, error) {
projectID, err := reqInt(c, "project_id")
if err != nil {
return nil, err
}
requirementID, err := reqInt(c, "requirement_id")
if err != nil {
return nil, err
}
return service.DeleteRequirement(c.Request.Context(), projectID, requirementID)
}))
g.PUT("/:project_id", handler("project", func(c *gin.Context) (interface{}, error) {
input := models.ProjectUpdate{}
err := c.BindJSON(&input)
if err != nil {
return nil, models.BadInputError{
Object: "ProjectUpdate",
Problem: "Invalid JSON: " + err.Error(),
}
}
id, err := reqInt(c, "project_id")
if err != nil {
return nil, err
}
return service.Update(c.Request.Context(), id, input)
}))
g.DELETE("/:project_id", handler("project", func(c *gin.Context) (interface{}, error) {
id, err := reqInt(c, "project_id")
if err != nil {
return nil, err
}
return service.Delete(c.Request.Context(), id)
}))
}

21
usecases/projects/result.go

@ -34,6 +34,16 @@ type Result struct {
Requirements []RequirementResult `json:"requirements"`
}
func (r *Result) Requirement(id int) *RequirementResult {
for _, requirement := range r.Requirements {
if id == requirement.ID {
return &requirement
}
}
return nil
}
type RequirementResult struct {
ID int `json:"id"`
Name string `json:"name"`
@ -42,6 +52,8 @@ type RequirementResult struct {
StatusName string `json:"statusName"`
Stats []RequirementResultStat `json:"stats"`
Items []items.Result `json:"items"`
Requirement entities.Requirement `json:"-"`
}
func (r *RequirementResult) Stat(id int) *RequirementResultStat {
@ -54,6 +66,13 @@ func (r *RequirementResult) Stat(id int) *RequirementResultStat {
return nil
}
func (r *RequirementResult) refresh(scope scopes.Result) {
r.Name = r.Requirement.Name
r.Description = r.Requirement.Description
r.Status = r.Requirement.Status
r.StatusName = scope.StatusName(r.Requirement.Status)
}
type RequirementResultStat struct {
ID int `json:"id"`
Name string `json:"name"`
@ -99,6 +118,8 @@ func generateRequirementResult(req entities.Requirement, scope scopes.Result, re
StatusName: scope.StatusName(req.Status),
Stats: make([]RequirementResultStat, 0, 8),
Items: make([]items.Result, 0, 8),
Requirement: req,
}
statIndices := make(map[int]int)
for _, reqStat := range requirementStats {

198
usecases/projects/service.go

@ -109,8 +109,8 @@ func (s *Service) Create(ctx context.Context, project entities.Project) (*Result
}
}
// This is stufflog 3, so allow importing and scripts to mess with the created time.
if project.CreatedTime.Year() < 2000 {
// Allow importing and scripts to mess with the created time, so only set it to now if it's not set.
if project.CreatedTime.IsZero() {
project.CreatedTime = time.Now()
}
@ -130,3 +130,197 @@ func (s *Service) Create(ctx context.Context, project entities.Project) (*Result
[]items.Result{},
), nil
}
func (s *Service) Update(ctx context.Context, id int, update models.ProjectUpdate) (*Result, error) {
project, err := s.Find(ctx, id)
if err != nil {
return nil, err
}
err = s.Repository.Update(ctx, project.Project, update)
if err != nil {
return nil, err
}
project.Update(update)
return project, nil
}
func (s *Service) Delete(ctx context.Context, id int) (*Result, error) {
project, err := s.Find(ctx, id)
if err != nil {
return nil, err
}
err = s.Repository.Delete(ctx, project.Project)
if err != nil {
return nil, err
}
return project, nil
}
func (s *Service) CreateRequirement(ctx context.Context, id int, requirement entities.Requirement, stats []entities.RequirementStat) (*RequirementResult, error) {
requirement.Name = strings.Trim(requirement.Name, "  \t\r\n")
if requirement.Name == "" {
return nil, models.BadInputError{
Object: "ProjectInput",
Field: "name",
Problem: "Empty name provided",
}
}
if !requirement.Status.Valid() {
return nil, models.BadInputError{
Object: "ProjectInput",
Field: "status",
Problem: "Non-existent status ID",
}
}
project, err := s.Find(ctx, id)
if err != nil {
return nil, err
}
requirement.ScopeID = project.ScopeID
requirement.ProjectID = project.ID
scope := s.Scopes.Context(ctx).Scope
for _, stat := range stats {
if !scope.HasStat(stat.StatID) {
return nil, models.BadInputError{
Object: "ProjectInput",
Field: "stats",
Problem: "Non-existent stat ID",
}
}
}
newRequirement, err := s.Repository.CreateRequirement(ctx, requirement)
if err != nil {
return nil, err
}
goodStats := make([]entities.RequirementStat, 0, len(stats))
for _, stat := range stats {
stat.RequirementID = newRequirement.ID
err = s.Repository.UpsertRequirementStat(ctx, stat)
if err == nil {
goodStats = append(goodStats, stat)
}
}
result := generateRequirementResult(
*newRequirement,
scope,
goodStats,
[]items.Result{},
)
return &result, nil
}
func (s *Service) UpdateRequirement(ctx context.Context, projectID, requirementID int, update models.RequirementUpdate) (*RequirementResult, error) {
project, err := s.Find(ctx, projectID)
if err != nil {
return nil, err
}
req := project.Requirement(requirementID)
if req == nil {
return nil, models.NotFoundError("Requirement")
}
err = s.Repository.UpdateRequirement(ctx, req.Requirement, update)
if err != nil {
return nil, err
}
req.Requirement.Update(update)
req.refresh(s.Scopes.Context(ctx).Scope)
return req, nil
}
func (s *Service) DeleteRequirement(ctx context.Context, projectID, requirementID int) (*RequirementResult, error) {
project, err := s.Find(ctx, projectID)
if err != nil {
return nil, err
}
req := project.Requirement(requirementID)
if req == nil {
return nil, models.NotFoundError("Requirement")
}
err = s.Repository.DeleteRequirement(ctx, req.Requirement)
if err != nil {
return nil, err
}
return req, nil
}
func (s *Service) UpsertRequirementStat(ctx context.Context, projectID, requirementID int, input entities.RequirementStat) (*RequirementResult, error) {
project, err := s.Find(ctx, projectID)
if err != nil {
return nil, err
}
req := project.Requirement(requirementID)
if req == nil {
return nil, models.NotFoundError("Requirement")
}
input.RequirementID = requirementID
scope := s.Scopes.Context(ctx).Scope
if !scope.HasStat(input.StatID) {
return nil, models.NotFoundError("Stat")
}
err = s.Repository.UpsertRequirementStat(ctx, input)
if err != nil {
return nil, err
}
project, err = s.Find(ctx, projectID)
if err != nil {
return nil, err
}
req = project.Requirement(requirementID)
if req == nil {
return nil, models.NotFoundError("Requirement")
}
return req, nil
}
func (s *Service) DeleteRequirementStat(ctx context.Context, projectID, requirementID, statID int) (*RequirementResult, error) {
project, err := s.Find(ctx, projectID)
if err != nil {
return nil, err
}
req := project.Requirement(requirementID)
if req == nil {
return nil, models.NotFoundError("Requirement")
}
stat := req.Stat(statID)
if stat == nil {
return nil, models.NotFoundError("Stat")
}
err = s.Repository.DeleteRequirementStat(ctx, entities.RequirementStat{
RequirementID: req.ID,
StatID: statID,
})
if err != nil {
return nil, err
}
for i := range req.Stats {
if req.Stats[i].ID == statID {
req.Stats = append(req.Stats[:i], req.Stats[i+1:]...)
break
}
}
return req, nil
}

20
usecases/scopes/result.go

@ -20,6 +20,26 @@ func (r *Result) MemberName(id string) string {
return ""
}
func (r *Result) Stat(id int) *ResultStat {
for _, stat := range r.Stats {
if stat.ID == id {
return &stat
}
}
return nil
}
func (r *Result) HasStat(id int) bool {
for _, stat := range r.Stats {
if stat.ID == id {
return true
}
}
return false
}
type ResultMember struct {
ID string `json:"id"`
Name string `json:"name"`

Loading…
Cancel
Save