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" "golang.org/x/sync/errgroup" "sort" ) type scopeRepository struct { db *sql.DB q *mysqlcore.Queries } func (r *scopeRepository) Find(ctx context.Context, id int, full bool) (*models.Scope, error) { scope, err := r.q.GetScope(ctx, id) if err != nil { if err == sql.ErrNoRows { return nil, slerrors.NotFound("Scope") } return nil, err } res := &models.Scope{ ScopeEntry: models.ScopeEntry{ ID: scope.ID, Name: scope.Name, Abbreviation: scope.Abbreviation, }, Members: []models.ScopeMember{}, Projects: []models.ProjectEntry{}, Stats: []models.Stat{}, StatusLabels: make(map[models.Status]string), } eg, ctx := errgroup.WithContext(ctx) if full { eg.Go(func() error { projects, err := r.q.ListProjectEntries(ctx, id) if err != nil && err != sql.ErrNoRows { return err } for _, project := range projects { res.Projects = append(res.Projects, models.ProjectEntry{ ID: project.ID, OwnerID: project.AuthorID, CreatedTime: project.CreatedTime, Name: project.Name, Status: models.Status(project.Status), }) } return nil }) eg.Go(func() error { stats, err := r.q.ListStats(ctx, id) if err != nil && err != sql.ErrNoRows { return err } for _, stat := range stats { var amounts map[string]int if stat.AllowedAmounts.Valid { amounts = make(map[string]int) err := json.Unmarshal(stat.AllowedAmounts.RawMessage, &amounts) if err != nil { return err } } res.Stats = append(res.Stats, models.Stat{ StatEntry: models.StatEntry{ ID: stat.ID, Name: stat.Name, Weight: stat.Weight, }, Description: stat.Description, AllowedAmounts: amounts, }) } return nil }) } eg.Go(func() error { members, err := r.q.ListScopeMembers(ctx, id) if err != nil && err != sql.ErrNoRows { return err } for _, member := range members { res.Members = append(res.Members, models.ScopeMember{ ID: member.UserID, Name: member.Name, Owner: member.Owner, }) } return nil }) err = eg.Wait() if err != nil { return nil, err } return res, nil } func (r *scopeRepository) List(ctx context.Context) ([]models.ScopeEntry, error) { scopes, err := r.q.ListScopes(ctx) if err != nil { return nil, err } res := make([]models.ScopeEntry, 0, len(scopes)) for _, scope := range scopes { res = append(res, models.ScopeEntry{ ID: scope.ID, Name: scope.Name, Abbreviation: scope.Abbreviation, }) } return res, nil } func (r *scopeRepository) ListByUser(ctx context.Context, userID string) ([]models.ScopeEntry, error) { scopes, err := r.q.ListScopesByUser(ctx, userID) if err != nil { return nil, err } res := make([]models.ScopeEntry, 0, len(scopes)) for _, scope := range scopes { res = append(res, models.ScopeEntry{ ID: scope.ID, Name: scope.Name, Abbreviation: scope.Abbreviation, }) } return res, nil } func (r *scopeRepository) Create(ctx context.Context, scope models.ScopeEntry, owner models.ScopeMember) (*models.Scope, 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.InsertScope(ctx, mysqlcore.InsertScopeParams(scope)) if err != nil { return nil, err } id, err := res.LastInsertId() if err != nil { return nil, err } err = q.UpdateScopeMember(ctx, mysqlcore.UpdateScopeMemberParams{ ScopeID: int(id), UserID: owner.ID, Name: owner.Name, Owner: true, }) err = tx.Commit() if err != nil { return nil, err } return &models.Scope{ ScopeEntry: models.ScopeEntry{ ID: int(id), Name: scope.Name, Abbreviation: scope.Abbreviation, }, DisplayName: owner.Name, Members: []models.ScopeMember{ { ID: owner.ID, Name: owner.Name, Owner: true, }, }, Projects: []models.ProjectEntry{}, Stats: []models.Stat{}, }, nil } func (r *scopeRepository) Update(ctx context.Context, scope models.ScopeEntry) error { return mysqlcore.New(r.db).UpdateScope(ctx, mysqlcore.UpdateScopeParams{ Name: scope.Name, Abbreviation: scope.Abbreviation, ID: scope.ID, }) } func (r *scopeRepository) Delete(ctx context.Context, scope models.Scope) error { tx, err := r.db.BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() q := r.q.WithTx(tx) err = q.DeleteAllScopeMembers(ctx, scope.ID) if err != nil { return err } err = q.DeleteScope(ctx, scope.ID) if err != nil { return err } return tx.Commit() } func (r *scopeRepository) UpdateMember(ctx context.Context, scope models.Scope, member models.ScopeMember) (*models.Scope, error) { err := mysqlcore.New(r.db).UpdateScopeMember(ctx, mysqlcore.UpdateScopeMemberParams{ ScopeID: scope.ID, UserID: member.ID, Name: member.Name, Owner: member.Owner, }) if err != nil { return nil, err } found := false scope.Members = append(scope.Members[:0:0], scope.Members...) for i, member2 := range scope.Members { if member2.ID == member.ID { scope.Members[i] = member2 found = true } } if !found { scope.Members = append(scope.Members, member) sort.Slice(scope.Members, func(i, j int) bool { return scope.Members[i].Name < scope.Members[j].Name }) } return &scope, nil } func (r *scopeRepository) DeleteMember(ctx context.Context, scope models.Scope, userID string) (*models.Scope, error) { err := mysqlcore.New(r.db).DeleteScopeMember(ctx, mysqlcore.DeleteScopeMemberParams{ ScopeID: scope.ID, UserID: userID, }) if err != nil { return nil, err } newMembers := make([]models.ScopeMember, 0, len(scope.Members)-1) for _, member := range scope.Members { if member.ID != userID { newMembers = append(newMembers, member) } } scope.Members = newMembers return &scope, nil }