Files
stufflog3/usecases/items/service.go
T
gisle c8ca2d4344
continuous-integration/drone/push Build is passing
add all-scopes item endpoint.
2022-07-08 17:34:43 +02:00

379 lines
8.6 KiB
Go
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package items
import (
"context"
"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/auth"
"git.aiterp.net/stufflog3/stufflog3/usecases/scopes"
"git.aiterp.net/stufflog3/stufflog3/usecases/stats"
"strings"
"time"
)
type Service struct {
Auth *auth.Service
Scopes *scopes.Service
Stats *stats.Service
Repository Repository
ProjectFetcher ProjectFetcher
RequirementFetcher RequirementFetcher
}
func (s *Service) Find(ctx context.Context, id int) (*Result, error) {
sc := s.Scopes.Context(ctx)
if sc == nil {
return nil, models.PermissionDeniedError{}
}
item, err := s.Repository.Find(ctx, sc.ID, id)
if err != nil {
return nil, err
}
itemStats, err := s.Repository.ListStat(ctx, *item)
if err != nil {
return nil, err
}
var requirements []entities.Requirement
if item.RequirementID != nil {
requirements, _, err = s.RequirementFetcher.FetchRequirements(ctx, item.ScopeID, *item.RequirementID)
if err != nil {
return nil, err
}
}
var projects []entities.Project
if item.ProjectID != nil {
projects, err = s.ProjectFetcher.FetchProjects(ctx, item.ScopeID, *item.ProjectID)
if err != nil {
return nil, err
}
}
result := generateResult(*item, sc.Scope, itemStats, requirements, projects)
return &result, nil
}
func (s *Service) ListAllScopes(ctx context.Context, filter models.ItemFilter) ([]Result, error) {
userScopes, err := s.Scopes.Context(ctx).Scopes(ctx)
if err != nil {
return nil, err
}
if len(userScopes) == 0 {
return []Result{}, nil
}
filter.ScopeIDs = make([]int, 0, len(userScopes))
for _, scope := range userScopes {
filter.ScopeIDs = append(filter.ScopeIDs, scope.ID)
}
items, err := s.Repository.Fetch(ctx, filter)
if err != nil {
return nil, err
}
progresses, err := s.Repository.ListStat(ctx, items...)
if err != nil {
return nil, err
}
reqIds := genutils.Set[int]{}
projIds := genutils.Set[int]{}
for _, item := range items {
if item.RequirementID != nil {
reqIds.Add(*item.RequirementID)
}
if item.ProjectID != nil {
projIds.Add(*item.ProjectID)
}
}
var requirements []entities.Requirement
if reqIds.Len() > 0 {
requirements, _, err = s.RequirementFetcher.FetchRequirements(ctx, -1, reqIds.Values()...)
if err != nil {
return nil, err
}
}
var projects []entities.Project
if projIds.Len() > 0 {
projects, err = s.ProjectFetcher.FetchProjects(ctx, -1, projIds.Values()...)
if err != nil {
return nil, err
}
}
res := make([]Result, 0, len(items))
for _, item := range items {
for _, scope := range userScopes {
if item.ScopeID == scope.ID {
res = append(res, generateResult(item, scope, progresses, requirements, projects))
break
}
}
}
return res, nil
}
func (s *Service) ListScoped(ctx context.Context, filter models.ItemFilter) ([]Result, error) {
sc := s.Scopes.Context(ctx)
if sc == nil {
return nil, models.PermissionDeniedError{}
}
if filter.ScopeIDs != nil {
userScopes, err := sc.Scopes(ctx)
if err != nil {
return nil, err
}
for _, id := range filter.ScopeIDs {
found := false
for _, scope := range userScopes {
if scope.ID == id {
found = true
break
}
}
if !found {
return nil, models.ForbiddenError("Scope you do not belong to found in filter scope IDs.")
}
}
} else {
filter.ScopeIDs = []int{sc.ID}
}
items, err := s.Repository.Fetch(ctx, filter)
if err != nil {
return nil, err
}
progresses, err := s.Repository.ListStat(ctx, items...)
if err != nil {
return nil, err
}
reqIds := genutils.Set[int]{}
projIds := genutils.Set[int]{}
for _, item := range items {
if item.RequirementID != nil {
reqIds.Add(*item.RequirementID)
}
if item.ProjectID != nil {
projIds.Add(*item.ProjectID)
}
}
var requirements []entities.Requirement
if reqIds.Len() > 0 {
requirements, _, err = s.RequirementFetcher.FetchRequirements(ctx, sc.ID, reqIds.Values()...)
if err != nil {
return nil, err
}
}
var projects []entities.Project
if projIds.Len() > 0 {
projects, err = s.ProjectFetcher.FetchProjects(ctx, sc.ID, projIds.Values()...)
if err != nil {
return nil, err
}
}
res := make([]Result, 0, len(items))
for _, item := range items {
res = append(res, generateResult(item, sc.Scope, progresses, requirements, projects))
}
return res, nil
}
func (s *Service) Create(ctx context.Context, item entities.Item, stats []entities.ItemStat) (*Result, error) {
scope := s.Scopes.Context(ctx).Scope
user := s.Auth.GetUser(ctx)
item.Name = strings.Trim(item.Name, "  \t\r\n")
if item.Name == "" {
return nil, models.BadInputError{
Object: "ItemInput",
Field: "name",
Problem: "Item name cannot be empty",
}
}
var requirements []entities.Requirement
var projects []entities.Project
if item.RequirementID != nil {
reqs, _, err := s.RequirementFetcher.FetchRequirements(ctx, scope.ID, *item.RequirementID)
if err != nil {
return nil, err
}
if len(reqs) == 0 {
return nil, models.NotFoundError("Requirement")
}
item.ProjectID = &reqs[0].ProjectID
requirements = reqs
projects, _ = s.ProjectFetcher.FetchProjects(ctx, scope.ID, *item.ProjectID)
}
item.ScopeID = scope.ID
item.OwnerID = user.ID
if item.CreatedTime.IsZero() {
item.CreatedTime = time.Now()
}
newItem, err := s.Repository.Insert(ctx, item)
if err != nil {
return nil, err
}
goodStats := make([]entities.ItemStat, 0, len(stats))
for _, stat := range stats {
stat.ItemID = newItem.ID
if scope.Stat(stat.StatID) == nil {
return nil, models.NotFoundError("Stat")
}
err := s.Repository.UpdateStat(ctx, stat)
if err != nil {
continue
}
goodStats = append(goodStats, stat)
}
result := generateResult(*newItem, scope, goodStats, requirements, projects)
return &result, nil
}
func (s *Service) Update(ctx context.Context, id int, update models.ItemUpdate, stats []entities.ItemStat) (*Result, error) {
scope := s.Scopes.Context(ctx).Scope
if update.OwnerID != nil {
if _, ok := scope.Members[*update.OwnerID]; !ok {
return nil, models.NotFoundError("Member")
}
}
if update.Name != nil && *update.Name == "" {
return nil, models.BadInputError{
Object: "ItemUpdate",
Field: "name",
Problem: "Item name cannot be empty",
}
}
item, err := s.Find(ctx, id)
if err != nil {
return nil, err
}
if update.RequirementID != nil {
if *update.RequirementID > 0 {
req, _, err := s.RequirementFetcher.FetchRequirements(ctx, scope.ID, *update.RequirementID)
if err != nil {
return nil, err
}
if len(req) == 0 {
return nil, models.NotFoundError("Requirement")
}
item.ProjectID = genutils.Ptr(req[0].ProjectID)
item.Requirement = &ResultRequirement{
ID: req[0].ID,
Name: req[0].Name,
Status: req[0].Status,
StatusName: scope.StatusName(req[0].Status),
}
projects, err := s.ProjectFetcher.FetchProjects(ctx, scope.ID, *item.ProjectID)
if err != nil {
return nil, err
}
if len(projects) == 1 {
item.Project = &ResultProject{
ID: projects[0].ID,
CreatedTime: projects[0].CreatedTime,
Name: projects[0].Name,
Status: projects[0].Status,
StatusName: scope.StatusName(projects[0].Status),
}
}
} else {
item.ProjectID = nil
}
}
err = s.Repository.Update(ctx, item.Item, update)
if err != nil {
return nil, err
}
item.Item.ApplyUpdate(update)
for _, stat := range stats {
stat.ItemID = item.ID
err = s.Repository.UpdateStat(ctx, stat)
if err == nil {
item.AddStat(scope, stat)
}
}
return item, nil
}
func (s *Service) Delete(ctx context.Context, id int) (*Result, error) {
item, err := s.Find(ctx, id)
if err != nil {
return nil, err
}
err = s.Repository.Delete(ctx, item.Item)
if err != nil {
return nil, err
}
return item, nil
}
func (s *Service) UpdateStat(ctx context.Context, itemID int, stat entities.ItemStat) (*Result, error) {
scope := s.Scopes.Context(ctx).Scope
item, err := s.Find(ctx, itemID)
if err != nil {
return nil, err
}
stat.ItemID = itemID
err = s.Repository.UpdateStat(ctx, stat)
if err != nil {
return nil, err
}
item.AddStat(scope, stat)
return item, nil
}
func (s *Service) DeleteStat(ctx context.Context, itemID int, statID int) (*Result, error) {
item, err := s.Find(ctx, itemID)
if err != nil {
return nil, err
}
err = s.Repository.UpdateStat(ctx, entities.ItemStat{
ItemID: itemID,
StatID: statID,
Acquired: 0,
Required: -1,
})
if err != nil {
return nil, err
}
item.RemoveStat(statID)
return item, nil
}