API package refactor

This commit is contained in:
Manuel Forcén Muñoz 2025-02-12 19:29:00 +01:00
parent a7cd86962e
commit bc6b57bc54
14 changed files with 474 additions and 416 deletions

9
core/errors.go Normal file
View file

@ -0,0 +1,9 @@
package core
import "errors"
var (
ErrNotMember = errors.New("User is not member of plan")
ErrNotFound = errors.New("Resource is not found")
ErrInvalidOption = errors.New("Option is not a valid one for this poll")
)

47
core/models.go Normal file
View file

@ -0,0 +1,47 @@
package core
type User struct {
Username string `gorm:"primaryKey" json:"username"`
Password string `json:"password"`
OwnedPlans []Plan `gorm:"foreignKey:Owner;references:Username" json:"-"`
MemberOf []Member `json:"-"`
Votes []Vote `gorm:"foreignKey:UsernameID" json:"-"`
}
type Member struct {
ID uint `gorm:"primaryKey;autoIncrement:true" json:"-"`
PlanID uint `json:"-"`
Plan Plan `json:"-"`
Type string `gorm:"check:type in ('user','non-user')" json:"type"`
Name string `gorm:"check:type=='member' OR name IS NOT NULL" json:"name"`
Status string `gorm:"check:status in ('pending','ready')" json:"status"`
JoinCode string `gorm:"uniqueIndex;check:type=='non-member' OR join_code IS NOT NULL" json:"join_code,omitempty"`
UserID string `json:"username"`
User User `gorm:"foreignKey:UserID" json:"-"`
}
// CREATE TABLE plans(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING, owner STRING, FOREIGN KEY(owner) REFERENCES users(username))
// CREATE TABLE plan_user_relations(username STRING, plan INTEGER, PRIMARY KEY(username, plan), FOREIGN KEY username REFERENCES user(username), FOREIGN KEY plan REFERENCES plans(id))
type Plan struct {
ID uint `gorm:"primaryKey;autoIncrement:true" json:"id"`
Name string `json:"name"`
Owner string `json:"owner"`
Description string `json:"description"`
Members []Member `json:"-"`
Polls []Poll `gorm:"foreignKey:PlanID;references:ID" json:"-"`
}
// CREATE TABLE polls(id INTEGER PRIMARY KEY AUTOINCREMENT, plan INTEGER, name STRING, options JSON, FOREIGN KEY plan REFERENCES plans(id))
type Poll struct {
ID uint `gorm:"primaryKey;autoIncrement:true" json:"id"`
PlanID uint `json:"-"`
Options string `json:"options"`
Votes []Vote `gorm:"foreignKey:PollID;references:ID" json:"-"`
}
// CREATE TABLE votes(id INTEGER, poll INTEGER, user STRING, value JSON, FOREIGN KEY poll REFERENCES polls(id), FOREIGN KEY user REFERENCES user(username))
type Vote struct {
PollID uint `gorm:"primaryKey" json:"-"`
UsernameID string `gorm:"primaryKey" json:"username_id"`
Value string `json:"value"`
}

129
core/plans.go Normal file
View file

@ -0,0 +1,129 @@
package core
import (
"crypto/rand"
"encoding/base64"
"errors"
"gorm.io/gorm"
)
func GetPlan(orm *gorm.DB, user User, id uint) (*Plan, error) {
var plan Plan = Plan{
ID: id,
}
result := orm.Take(&plan)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, ErrNotFound
} else if result.Error != nil {
return nil, result.Error
}
if plan.Owner == user.Username {
return &plan, nil
}
isMember, err := plan.IsMember(orm, &user)
if !isMember || err != nil {
return nil, err
}
return &plan, nil
}
func (p *Plan) GetAllUsers(orm *gorm.DB) ([]Member, error) {
var members []Member
err := orm.Model(p).Association("Members").Find(&members)
return members, err
}
func (p *Plan) IsMember(orm *gorm.DB, u *User) (bool, error) {
var member_count int64
result := orm.
Table("members").
Where("user_id=? AND plan_id=?", u.Username, p.ID).
Count(&member_count).Error
if result != nil {
return false, ErrNotMember
}
return member_count == 1, nil
}
func (p *Plan) GetMember(orm *gorm.DB, u *User) (*Member, error) {
var m Member
result := orm.
Table("members").
Where("user_id=? AND plan_id=?", u.Username, p.ID).
Take(&m).Error
if result != nil {
return nil, ErrNotMember
}
return &m, nil
}
func (p *Plan) HasNonUser(orm *gorm.DB, name string) (bool, error) {
var member_count int64
result := orm.
Table("members").
Where("plan_id=? AND name=?", p.ID, name).
Count(&member_count).Error
if result != nil {
return false, result
}
return member_count == 1, nil
}
func (p *Plan) AddMember(orm *gorm.DB, new_member *Member) error {
if new_member == nil {
return errors.New("Member is nil")
}
new_member.PlanID = p.ID
if new_member.Type == "non-user" {
found, err := p.HasNonUser(orm, new_member.Name)
if err != nil {
return nil
}
if found {
return errors.New("Non user name taken")
}
new_member.Status = "ready"
return orm.Create(&new_member).Error
} else if new_member.Type == "user" {
user, err := GetUser(orm, new_member.UserID)
if err != nil {
return err
}
found, err := p.IsMember(orm, &user)
if err != nil {
return nil
}
if found {
return errors.New("User already is member")
}
new_member.Status = "pending"
var res error
for retries := 3; retries >= 0; retries -= 1 {
join_code := make([]byte, 32)
_, err = rand.Read(join_code)
if err != nil {
return err
}
new_member.JoinCode = base64.URLEncoding.EncodeToString(join_code)
res = orm.Create(&new_member).Error
if res == nil {
break
} else if !errors.Is(res, gorm.ErrDuplicatedKey) {
return res
}
}
return res
} else {
return errors.New("Invalid type for user")
}
}

43
core/polls.go Normal file
View file

@ -0,0 +1,43 @@
package core
import (
"fmt"
"strings"
"gorm.io/gorm"
)
func GetPoll(orm *gorm.DB, user User, id uint) (*Poll, error) {
var poll Poll = Poll{
ID: id,
}
if result := orm.Take(&poll); result.Error != nil {
return nil, result.Error
}
fmt.Printf("%+v\n", poll.PlanID)
return &poll, nil
}
func (p *Poll) SetVote(orm *gorm.DB, user User, option string) error {
found := false
options := strings.Split(p.Options, ",")
for _, opt := range options {
if opt == option {
found = true
break
}
}
if !found {
return ErrInvalidOption
}
if res := orm.Create(Vote{PollID: p.ID, UsernameID: user.Username, Value: option}); res.Error != nil {
return res.Error
}
return nil
}

23
core/users.go Normal file
View file

@ -0,0 +1,23 @@
package core
import (
"gorm.io/gorm"
)
func (u *User) GetPlans(orm *gorm.DB) ([]Plan, error) {
var plans []Plan
err := orm.Debug().Table("plans p").
Select("p.*").
Joins("JOIN members m ON m.plan_id=p.id").
Where("m.user_id=?", u.Username).
Find(&plans)
return plans, err.Error
}
func GetUser(orm *gorm.DB, username string) (User, error) {
user := User{Username: username}
if err := orm.Take(&user).Error; err != nil {
return user, err
}
return user, nil
}