Skip to content

Commit

Permalink
Merge branch 'main' into SAC-30-Club-Event-Vector-Embeddings
Browse files Browse the repository at this point in the history
  • Loading branch information
garrettladley authored Feb 17, 2024
2 parents d710b0b + e319ab1 commit eabc876
Show file tree
Hide file tree
Showing 32 changed files with 1,231 additions and 806 deletions.
15 changes: 11 additions & 4 deletions backend/src/controllers/club.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package controllers

import (
"strconv"
"fmt"

"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
Expand All @@ -18,10 +18,17 @@ func NewClubController(clubService services.ClubServiceInterface) *ClubControlle
}

func (cl *ClubController) GetAllClubs(c *fiber.Ctx) error {
defaultLimit := 10
defaultPage := 1
var queryParams models.ClubQueryParams

clubs, err := cl.clubService.GetClubs(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage)))
queryParams.Limit = 10 // default limit
queryParams.Page = 1 // default page

if err := c.QueryParser(&queryParams); err != nil {
fmt.Println(err)
return errors.FailedtoParseQueryParams.FiberError(c)
}

clubs, err := cl.clubService.GetClubs(&queryParams)
if err != nil {
return err.FiberError(c)
}
Expand Down
2 changes: 1 addition & 1 deletion backend/src/controllers/user_member.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (um *UserMemberController) DeleteMembership(c *fiber.Ctx) error {
}

func (um *UserMemberController) GetMembership(c *fiber.Ctx) error {
followers, err := um.clubMemberService.GetMembership(c.Params("clubID"))
followers, err := um.clubMemberService.GetMembership(c.Params("userID"))
if err != nil {
return err.FiberError(c)
}
Expand Down
4 changes: 4 additions & 0 deletions backend/src/errors/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ var (
StatusCode: fiber.StatusBadRequest,
Message: "failed to parse request body",
}
FailedtoParseQueryParams = Error{
StatusCode: fiber.StatusBadRequest,
Message: "failed to parse query params",
}
FailedToValidateID = Error{
StatusCode: fiber.StatusBadRequest,
Message: "failed to validate id",
Expand Down
8 changes: 8 additions & 0 deletions backend/src/errors/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,16 @@ var (
StatusCode: fiber.StatusInternalServerError,
Message: "failed to get user memberships",
}
UserNotMemberOfClub = Error{
StatusCode: fiber.StatusNotFound,
Message: "user not member of club",
}
FailedToGetUserFollowing = Error{
StatusCode: fiber.StatusInternalServerError,
Message: "failed to get user following",
}
UserNotFollowingClub = Error{
StatusCode: fiber.StatusNotFound,
Message: "user not following club",
}
)
37 changes: 36 additions & 1 deletion backend/src/models/club.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package models

import (
"fmt"
"strings"

"github.com/google/uuid"
"gorm.io/gorm"
)
Expand Down Expand Up @@ -38,7 +41,7 @@ type Club struct {
Logo string `gorm:"type:varchar(255);default:NULL" json:"logo" validate:"omitempty,http_url,s3_url,max=255"` // S3 URL

Parent *uuid.UUID `gorm:"foreignKey:Parent" json:"-" validate:"uuid4"`
Tag []Tag `gorm:"many2many:club_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
Tag []Tag `gorm:"many2many:club_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"tags,omitempty" validate:"-"`
// User
Admin []User `gorm:"many2many:user_club_admins;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"required"`
Member []User `gorm:"many2many:user_club_members;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"required"`
Expand Down Expand Up @@ -79,6 +82,38 @@ type CreateClubTagsRequestBody struct {
Tags []uuid.UUID `json:"tags" validate:"required"`
}

type ClubQueryParams struct {
Tags []string `query:"tags"`
MinMembers int `query:"min_members"`
MaxMembers int `query:"max_members"`
RecruitmentCycle *RecruitmentCycle `query:"recruitment_cycle"`
IsRecruiting *bool `query:"is_recruiting"`
Limit int `query:"limit"`
Page int `query:"page"`
}

func (cqp *ClubQueryParams) IntoWhere() string {
conditions := make([]string, 0)

if cqp.MinMembers != 0 {
conditions = append(conditions, fmt.Sprintf("num_members >= %d", cqp.MinMembers))
}
if cqp.MaxMembers != 0 {
conditions = append(conditions, fmt.Sprintf("num_members <= %d", cqp.MaxMembers))
}
if cqp.RecruitmentCycle != nil {
conditions = append(conditions, fmt.Sprintf("recruitment_cycle = '%s'", *cqp.RecruitmentCycle))
}
if cqp.IsRecruiting != nil {
conditions = append(conditions, fmt.Sprintf("is_recruiting = %t", *cqp.IsRecruiting))
}

if len(conditions) == 0 {
return ""
}
return "WHERE " + strings.Join(conditions, " AND ")
}

func (c *Club) AfterCreate(tx *gorm.DB) (err error) {
tx.Model(&c).Update("num_members", c.NumMembers+1)
return
Expand Down
4 changes: 2 additions & 2 deletions backend/src/server/routes/club_member.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
func ClubMember(clubsIDRouter fiber.Router, clubMemberService services.ClubMemberServiceInterface) {
clubMemberController := controllers.NewClubMemberController(clubMemberService)

clubMember := clubsIDRouter.Group("/member")
clubMember := clubsIDRouter.Group("/members")

// api/v1/clubs/:clubID/member/*
// api/v1/clubs/:clubID/members/*
clubMember.Get("/", clubMemberController.GetClubMembers)
}
2 changes: 1 addition & 1 deletion backend/src/server/routes/club_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func ClubTag(router fiber.Router, clubTagService services.ClubTagServiceInterface) {
clubTagController := controllers.NewClubTagController(clubTagService)

clubTags := router.Group("/:clubID/tags")
clubTags := router.Group("/tags")

clubTags.Post("/", clubTagController.CreateClubTags)
clubTags.Get("/", clubTagController.GetClubTags)
Expand Down
4 changes: 2 additions & 2 deletions backend/src/server/routes/user_member.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func UserMember(usersRouter fiber.Router, userMembershipService services.UserMem
userMember := usersRouter.Group("/:userID/member")

// api/v1/users/:userID/member/*
userMember.Post("/", userMemberController.CreateMembership)
userMember.Delete("/", userMemberController.DeleteMembership)
userMember.Post("/:clubID", userMemberController.CreateMembership)
userMember.Delete("/:clubID", userMemberController.DeleteMembership)
userMember.Get("/", userMemberController.GetMembership)
}
14 changes: 5 additions & 9 deletions backend/src/services/club.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

type ClubServiceInterface interface {
GetClubs(limit string, page string) ([]models.Club, *errors.Error)
GetClubs(queryParams *models.ClubQueryParams) ([]models.Club, *errors.Error)
GetClub(id string) (*models.Club, *errors.Error)
CreateClub(clubBody models.CreateClubRequestBody) (*models.Club, *errors.Error)
UpdateClub(id string, clubBody models.UpdateClubRequestBody) (*models.Club, *errors.Error)
Expand All @@ -27,20 +27,16 @@ func NewClubService(db *gorm.DB, validate *validator.Validate) *ClubService {
return &ClubService{DB: db, Validate: validate}
}

func (c *ClubService) GetClubs(limit string, page string) ([]models.Club, *errors.Error) {
limitAsInt, err := utilities.ValidateNonNegative(limit)
if err != nil {
func (c *ClubService) GetClubs(queryParams *models.ClubQueryParams) ([]models.Club, *errors.Error) {
if queryParams.Limit < 0 {
return nil, &errors.FailedToValidateLimit
}

pageAsInt, err := utilities.ValidateNonNegative(page)
if err != nil {
if queryParams.Page < 0 {
return nil, &errors.FailedToValidatePage
}

offset := (*pageAsInt - 1) * *limitAsInt

return transactions.GetClubs(c.DB, *limitAsInt, offset)
return transactions.GetClubs(c.DB, queryParams)
}

func (c *ClubService) CreateClub(clubBody models.CreateClubRequestBody) (*models.Club, *errors.Error) {
Expand Down
36 changes: 31 additions & 5 deletions backend/src/transactions/club.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,28 @@ func GetAdminIDs(db *gorm.DB, clubID uuid.UUID) ([]uuid.UUID, *errors.Error) {
return adminUUIDs, nil
}

func GetClubs(db *gorm.DB, limit int, offset int) ([]models.Club, *errors.Error) {
func GetClubs(db *gorm.DB, queryParams *models.ClubQueryParams) ([]models.Club, *errors.Error) {
query := db.Model(&models.Club{})

if queryParams.Tags != nil && len(queryParams.Tags) > 0 {
query = query.Preload("Tags")
}

for key, value := range queryParams.IntoWhere() {
query = query.Where(key, value)
}

if queryParams.Tags != nil && len(queryParams.Tags) > 0 {
query = query.Joins("JOIN club_tags ON club_tags.club_id = clubs.id").
Where("club_tags.tag_id IN ?", queryParams.Tags).
Group("clubs.id") // ensure unique club records
}

var clubs []models.Club
result := db.Limit(limit).Offset(offset).Find(&clubs)

offset := (queryParams.Page - 1) * queryParams.Limit

result := query.Limit(queryParams.Limit).Offset(offset).Find(&clubs)
if result.Error != nil {
return nil, &errors.FailedToGetClubs
}
Expand All @@ -38,7 +57,7 @@ func GetClubs(db *gorm.DB, limit int, offset int) ([]models.Club, *errors.Error)
func CreateClub(db *gorm.DB, userId uuid.UUID, club models.Club) (*models.Club, *errors.Error) {
user, err := GetUser(db, userId)
if err != nil {
return nil, &errors.UserNotFound
return nil, err
}

tx := db.Begin()
Expand All @@ -61,9 +80,16 @@ func CreateClub(db *gorm.DB, userId uuid.UUID, club models.Club) (*models.Club,
return &club, nil
}

func GetClub(db *gorm.DB, id uuid.UUID) (*models.Club, *errors.Error) {
func GetClub(db *gorm.DB, id uuid.UUID, preloads ...OptionalQuery) (*models.Club, *errors.Error) {
var club models.Club
if err := db.First(&club, id).Error; err != nil {

query := db

for _, preload := range preloads {
query = preload(query)
}

if err := query.First(&club, id).Error; err != nil {
if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
return nil, &errors.ClubNotFound
} else {
Expand Down
4 changes: 2 additions & 2 deletions backend/src/transactions/club_follower.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import (
func GetClubFollowers(db *gorm.DB, clubID uuid.UUID, limit int, page int) ([]models.User, *errors.Error) {
club, err := GetClub(db, clubID)
if err != nil {
return nil, &errors.ClubNotFound
return nil, err
}

var users []models.User

if err := db.Model(&club).Association("Followers").Find(&users); err != nil {
if err := db.Model(&club).Association("Follower").Find(&users); err != nil {
return nil, &errors.FailedToGetClubFollowers
}

Expand Down
4 changes: 2 additions & 2 deletions backend/src/transactions/club_member.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
func GetClubMembers(db *gorm.DB, clubID uuid.UUID, limit int, page int) ([]models.User, *errors.Error) {
club, err := GetClub(db, clubID)
if err != nil {
return nil, &errors.ClubNotFound
return nil, err
}

var users []models.User

if err := db.Model(&club).Association("Members").Find(&users); err != nil {
if err := db.Model(&club).Association("Member").Find(&users); err != nil {
return nil, &errors.FailedToGetClubMembers
}

Expand Down
15 changes: 6 additions & 9 deletions backend/src/transactions/club_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,25 @@ import (
"gorm.io/gorm"
)

// Create tags for a club
func CreateClubTags(db *gorm.DB, id uuid.UUID, tags []models.Tag) ([]models.Tag, *errors.Error) {
user, err := GetClub(db, id)
user, err := GetClub(db, id, PreloadTag())
if err != nil {
return nil, &errors.UserNotFound
return nil, err
}

if err := db.Model(&user).Association("Tag").Replace(tags); err != nil {
if err := db.Model(&user).Association("Tag").Append(tags); err != nil {
return nil, &errors.FailedToUpdateUser
}

return tags, nil
}

// Get tags for a club
func GetClubTags(db *gorm.DB, id uuid.UUID) ([]models.Tag, *errors.Error) {
var tags []models.Tag

club, err := GetClub(db, id)
if err != nil {
return nil, &errors.ClubNotFound
return nil, err
}

if err := db.Model(&club).Association("Tag").Find(&tags); err != nil {
Expand All @@ -37,16 +35,15 @@ func GetClubTags(db *gorm.DB, id uuid.UUID) ([]models.Tag, *errors.Error) {
return tags, nil
}

// Delete tag for a club
func DeleteClubTag(db *gorm.DB, id uuid.UUID, tagId uuid.UUID) *errors.Error {
club, err := GetClub(db, id)
if err != nil {
return &errors.ClubNotFound
return err
}

tag, err := GetTag(db, tagId)
if err != nil {
return &errors.TagNotFound
return err
}

if err := db.Model(&club).Association("Tag").Delete(&tag); err != nil {
Expand Down
23 changes: 23 additions & 0 deletions backend/src/transactions/preloaders.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package transactions

import "gorm.io/gorm"

type OptionalQuery func(*gorm.DB) *gorm.DB

func PreloadFollwer() OptionalQuery {
return func(db *gorm.DB) *gorm.DB {
return db.Preload("Follower")
}
}

func PreloadMember() OptionalQuery {
return func(db *gorm.DB) *gorm.DB {
return db.Preload("Member")
}
}

func PreloadTag() OptionalQuery {
return func(db *gorm.DB) *gorm.DB {
return db.Preload("Tag")
}
}
11 changes: 9 additions & 2 deletions backend/src/transactions/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,16 @@ func GetUsers(db *gorm.DB, limit int, offset int) ([]models.User, *errors.Error)
return users, nil
}

func GetUser(db *gorm.DB, id uuid.UUID) (*models.User, *errors.Error) {
func GetUser(db *gorm.DB, id uuid.UUID, preloads ...OptionalQuery) (*models.User, *errors.Error) {
var user models.User
if err := db.Omit("password_hash").First(&user, id).Error; err != nil {

query := db

for _, preload := range preloads {
query = preload(query)
}

if err := query.Omit("password_hash").First(&user, id).Error; err != nil {
if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
return nil, &errors.UserNotFound
} else {
Expand Down
Loading

0 comments on commit eabc876

Please sign in to comment.