From a45923278bdb130132252258594d765fedfc4952 Mon Sep 17 00:00:00 2001 From: Christian Carlsson Date: Fri, 20 Dec 2024 21:23:39 +0000 Subject: [PATCH] refact: rpc: parser pkg (#274) --- server/rpc/handlers/v1/exercise.go | 13 +- server/rpc/handlers/v1/feed.go | 3 +- server/rpc/handlers/v1/notification.go | 3 +- server/rpc/handlers/v1/routine.go | 7 +- server/rpc/handlers/v1/user.go | 9 +- server/rpc/handlers/v1/workout.go | 11 +- server/rpc/{handlers/v1 => parser}/parser.go | 258 ++++++++++--------- 7 files changed, 156 insertions(+), 148 deletions(-) rename server/rpc/{handlers/v1 => parser}/parser.go (53%) diff --git a/server/rpc/handlers/v1/exercise.go b/server/rpc/handlers/v1/exercise.go index 56215278..aa5ee6b2 100644 --- a/server/rpc/handlers/v1/exercise.go +++ b/server/rpc/handlers/v1/exercise.go @@ -13,6 +13,7 @@ import ( apiv1 "github.com/crlssn/getstronger/server/gen/proto/api/v1" "github.com/crlssn/getstronger/server/gen/proto/api/v1/apiv1connect" "github.com/crlssn/getstronger/server/repo" + "github.com/crlssn/getstronger/server/rpc/parser" "github.com/crlssn/getstronger/server/xcontext" "github.com/crlssn/getstronger/server/xzap" ) @@ -61,7 +62,7 @@ func (h *exerciseHandler) GetExercise(ctx context.Context, req *connect.Request[ } return connect.NewResponse(&apiv1.GetExerciseResponse{ - Exercise: parseExerciseToPB(exercise), + Exercise: parser.ExerciseToPB(exercise), }), nil } @@ -112,7 +113,7 @@ func (h *exerciseHandler) UpdateExercise(ctx context.Context, req *connect.Reque log.Info("exercise updated") return connect.NewResponse(&apiv1.UpdateExerciseResponse{ - Exercise: parseExerciseToPB(exercise), + Exercise: parser.ExerciseToPB(exercise), }), nil } @@ -174,7 +175,7 @@ func (h *exerciseHandler) ListExercises(ctx context.Context, req *connect.Reques log.Info("exercises listed") return connect.NewResponse(&apiv1.ListExercisesResponse{ - Exercises: parseExerciseSliceToPB(pagination.Items), + Exercises: parser.ExercisesToPB(pagination.Items), Pagination: &apiv1.PaginationResponse{ NextPageToken: pagination.NextPageToken, }, @@ -229,7 +230,7 @@ func (h *exerciseHandler) GetPreviousWorkoutSets(ctx context.Context, req *conne return nil, connect.NewError(connect.CodeInternal, nil) } - exerciseSets, err := parseSetSliceToExerciseSetsPB(sets, exercises) + exerciseSets, err := parser.ExerciseSetSlicesToPB(exercises, sets) if err != nil { log.Error("failed to parse set slice to exercise sets", zap.Error(err)) return nil, connect.NewError(connect.CodeInternal, nil) @@ -262,7 +263,7 @@ func (h *exerciseHandler) GetPersonalBests(ctx context.Context, req *connect.Req return nil, connect.NewError(connect.CodeInternal, nil) } - personalBestSlice, err := parseExerciseSetSlicesToPB(exercises, personalBests) + personalBestSlice, err := parser.ExerciseSetSliceToPB(exercises, personalBests) if err != nil { log.Error("failed to parse personal best slice to pb", zap.Error(err)) return nil, connect.NewError(connect.CodeInternal, nil) @@ -295,7 +296,7 @@ func (h *exerciseHandler) ListSets(ctx context.Context, req *connect.Request[api return nil, connect.NewError(connect.CodeInternal, nil) } - setSlice, err := parseSetSliceToPB(paginated.Items, nil) + setSlice, err := parser.SetsToPB(paginated.Items, nil) if err != nil { log.Error("failed to parse set slice to pb", zap.Error(err)) return nil, connect.NewError(connect.CodeInternal, nil) diff --git a/server/rpc/handlers/v1/feed.go b/server/rpc/handlers/v1/feed.go index 4cf6a517..43f8d1c4 100644 --- a/server/rpc/handlers/v1/feed.go +++ b/server/rpc/handlers/v1/feed.go @@ -11,6 +11,7 @@ import ( apiv1 "github.com/crlssn/getstronger/server/gen/proto/api/v1" "github.com/crlssn/getstronger/server/gen/proto/api/v1/apiv1connect" "github.com/crlssn/getstronger/server/repo" + "github.com/crlssn/getstronger/server/rpc/parser" "github.com/crlssn/getstronger/server/xcontext" ) @@ -89,7 +90,7 @@ func (h *feedHandler) ListFeedItems(ctx context.Context, req *connect.Request[ap mapPersonalBests[pb.ID] = struct{}{} } - feedItems, err := parseFeedItemsToPB(paginated.Items, exercises, mapPersonalBests) + feedItems, err := parser.FeedItemsToPB(paginated.Items, exercises, mapPersonalBests) if err != nil { log.Error("failed to parse feed items", zap.Error(err)) return nil, connect.NewError(connect.CodeInternal, nil) diff --git a/server/rpc/handlers/v1/notification.go b/server/rpc/handlers/v1/notification.go index 9685529b..99a5864e 100644 --- a/server/rpc/handlers/v1/notification.go +++ b/server/rpc/handlers/v1/notification.go @@ -12,6 +12,7 @@ import ( apiv1 "github.com/crlssn/getstronger/server/gen/proto/api/v1" "github.com/crlssn/getstronger/server/gen/proto/api/v1/apiv1connect" "github.com/crlssn/getstronger/server/repo" + "github.com/crlssn/getstronger/server/rpc/parser" "github.com/crlssn/getstronger/server/stream" "github.com/crlssn/getstronger/server/xcontext" ) @@ -87,7 +88,7 @@ func (h *notificationHandler) ListNotifications(ctx context.Context, req *connec return &connect.Response[apiv1.ListNotificationsResponse]{ Msg: &apiv1.ListNotificationsResponse{ - Notifications: parseNotificationSliceToPB(paginated.Items, nPayloads, users, workouts), + Notifications: parser.NotificationsToPB(paginated.Items, nPayloads, users, workouts), Pagination: &apiv1.PaginationResponse{ NextPageToken: paginated.NextPageToken, }, diff --git a/server/rpc/handlers/v1/routine.go b/server/rpc/handlers/v1/routine.go index 04bbd23f..801f0635 100644 --- a/server/rpc/handlers/v1/routine.go +++ b/server/rpc/handlers/v1/routine.go @@ -13,6 +13,7 @@ import ( apiv1 "github.com/crlssn/getstronger/server/gen/proto/api/v1" "github.com/crlssn/getstronger/server/gen/proto/api/v1/apiv1connect" "github.com/crlssn/getstronger/server/repo" + "github.com/crlssn/getstronger/server/rpc/parser" "github.com/crlssn/getstronger/server/xcontext" ) @@ -83,7 +84,7 @@ func (h *routineHandler) GetRoutine(ctx context.Context, req *connect.Request[ap log.Info("routine returned") return connect.NewResponse(&apiv1.GetRoutineResponse{ - Routine: parseRoutineToPB(routine), + Routine: parser.RoutineToPB(routine), }), nil } @@ -148,7 +149,7 @@ func (h *routineHandler) UpdateRoutine(ctx context.Context, req *connect.Request log.Info("routine updated") return connect.NewResponse(&apiv1.UpdateRoutineResponse{ - Routine: parseRoutineToPB(routine), + Routine: parser.RoutineToPB(routine), }), nil } @@ -202,7 +203,7 @@ func (h *routineHandler) ListRoutines(ctx context.Context, req *connect.Request[ log.Info("routines listed") return connect.NewResponse(&apiv1.ListRoutinesResponse{ - Routines: parseRoutineSliceToPB(pagination.Items), + Routines: parser.RoutinesToPB(pagination.Items), Pagination: &apiv1.PaginationResponse{ NextPageToken: pagination.NextPageToken, }, diff --git a/server/rpc/handlers/v1/user.go b/server/rpc/handlers/v1/user.go index 456da5e4..997cd287 100644 --- a/server/rpc/handlers/v1/user.go +++ b/server/rpc/handlers/v1/user.go @@ -13,6 +13,7 @@ import ( "github.com/crlssn/getstronger/server/pubsub" "github.com/crlssn/getstronger/server/pubsub/payloads" "github.com/crlssn/getstronger/server/repo" + "github.com/crlssn/getstronger/server/rpc/parser" "github.com/crlssn/getstronger/server/xcontext" ) @@ -48,7 +49,7 @@ func (h *userHandler) GetUser(ctx context.Context, req *connect.Request[apiv1.Ge return &connect.Response[apiv1.GetUserResponse]{ Msg: &apiv1.GetUserResponse{ - User: parseUserToPB(user, followed), + User: parser.UserToPB(user, followed), }, }, nil } @@ -77,7 +78,7 @@ func (h *userHandler) SearchUsers(ctx context.Context, req *connect.Request[apiv log.Info("searched users") return &connect.Response[apiv1.SearchUsersResponse]{ Msg: &apiv1.SearchUsersResponse{ - Users: parseUserSliceToPB(pagination.Items), + Users: parser.UsersToPB(pagination.Items), Pagination: &apiv1.PaginationResponse{ NextPageToken: pagination.NextPageToken, }, @@ -135,7 +136,7 @@ func (h *userHandler) ListFollowers(ctx context.Context, req *connect.Request[ap return &connect.Response[apiv1.ListFollowersResponse]{ Msg: &apiv1.ListFollowersResponse{ - Followers: parseUserSliceToPB(followers), + Followers: parser.UsersToPB(followers), }, }, nil } @@ -151,7 +152,7 @@ func (h *userHandler) ListFollowees(ctx context.Context, req *connect.Request[ap return &connect.Response[apiv1.ListFolloweesResponse]{ Msg: &apiv1.ListFolloweesResponse{ - Followees: parseUserSliceToPB(followees), + Followees: parser.UsersToPB(followees), }, }, nil } diff --git a/server/rpc/handlers/v1/workout.go b/server/rpc/handlers/v1/workout.go index f0cac8e2..7b4a24ef 100644 --- a/server/rpc/handlers/v1/workout.go +++ b/server/rpc/handlers/v1/workout.go @@ -17,6 +17,7 @@ import ( "github.com/crlssn/getstronger/server/pubsub" "github.com/crlssn/getstronger/server/pubsub/payloads" "github.com/crlssn/getstronger/server/repo" + "github.com/crlssn/getstronger/server/rpc/parser" "github.com/crlssn/getstronger/server/xcontext" ) @@ -56,7 +57,7 @@ func (h *workoutHandler) CreateWorkout(ctx context.Context, req *connect.Request UserID: userID, StartedAt: req.Msg.GetStartedAt().AsTime(), FinishedAt: req.Msg.GetFinishedAt().AsTime(), - ExerciseSets: parseExerciseSetsFromPB(req.Msg.GetExerciseSets()), + ExerciseSets: parser.ExercisesFromPB(req.Msg.GetExerciseSets()), }) if err != nil { log.Error("failed to create workout", zap.Error(err)) @@ -119,7 +120,7 @@ func (h *workoutHandler) GetWorkout(ctx context.Context, req *connect.Request[ap mapPersonalBests[set.ID] = struct{}{} } - w, err := parseWorkoutToPB(workout, exercises, users, mapPersonalBests) + w, err := parser.WorkoutToPB(workout, exercises, users, mapPersonalBests) if err != nil { log.Error("failed to parse workout", zap.Error(err)) return nil, connect.NewError(connect.CodeInternal, nil) @@ -191,7 +192,7 @@ func (h *workoutHandler) ListWorkouts(ctx context.Context, req *connect.Request[ mapPersonalBests[pb.ID] = struct{}{} } - w, err := parseWorkoutSliceToPB(pagination.Items, exercises, users, mapPersonalBests) + w, err := parser.WorkoutsToPB(pagination.Items, exercises, users, mapPersonalBests) if err != nil { log.Error("failed to parse workouts", zap.Error(err)) return nil, connect.NewError(connect.CodeInternal, nil) @@ -257,7 +258,7 @@ func (h *workoutHandler) PostComment(ctx context.Context, req *connect.Request[a log.Info("workout comment posted") return &connect.Response[apiv1.PostCommentResponse]{ Msg: &apiv1.PostCommentResponse{ - Comment: parseWorkoutCommentToPB(comment, user), + Comment: parser.WorkoutCommentToPB(comment, user), }, }, nil } @@ -293,7 +294,7 @@ func (h *workoutHandler) UpdateWorkout(ctx context.Context, req *connect.Request return fmt.Errorf("failed to update workout: %w", err) } - exerciseSets := parseExerciseSetsFromPB(req.Msg.GetWorkout().GetExerciseSets()) + exerciseSets := parser.ExercisesFromPB(req.Msg.GetWorkout().GetExerciseSets()) if err = tx.UpdateWorkoutSets(ctx, workout.ID, exerciseSets); err != nil { return fmt.Errorf("failed to update workout sets: %w", err) } diff --git a/server/rpc/handlers/v1/parser.go b/server/rpc/parser/parser.go similarity index 53% rename from server/rpc/handlers/v1/parser.go rename to server/rpc/parser/parser.go index 7d74a7f0..e6aed8bd 100644 --- a/server/rpc/handlers/v1/parser.go +++ b/server/rpc/parser/parser.go @@ -1,4 +1,4 @@ -package v1 +package parser import ( "fmt" @@ -11,16 +11,11 @@ import ( "github.com/crlssn/getstronger/server/safe" ) -func parseExerciseSliceToPB(exercises orm.ExerciseSlice) []*apiv1.Exercise { - pbExercises := make([]*apiv1.Exercise, 0, len(exercises)) - for _, exercise := range exercises { - pbExercises = append(pbExercises, parseExerciseToPB(exercise)) +func ExerciseToPB(exercise *orm.Exercise) *apiv1.Exercise { + if exercise == nil { + return nil } - return pbExercises -} - -func parseExerciseToPB(exercise *orm.Exercise) *apiv1.Exercise { return &apiv1.Exercise{ Id: exercise.ID, UserId: exercise.UserID, @@ -29,19 +24,38 @@ func parseExerciseToPB(exercise *orm.Exercise) *apiv1.Exercise { } } -func parseRoutineSliceToPB(routines orm.RoutineSlice) []*apiv1.Routine { - pbRoutines := make([]*apiv1.Routine, 0, len(routines)) - for _, routine := range routines { - pbRoutines = append(pbRoutines, parseRoutineToPB(routine)) +func ExercisesToPB(exercises orm.ExerciseSlice) []*apiv1.Exercise { + return mapSlice(exercises, ExerciseToPB) +} + +func UserToPB(user *orm.User, followed bool) *apiv1.User { + if user == nil { + return nil + } + + return &apiv1.User{ + Id: user.ID, + Email: safeGetEmail(user), + FirstName: user.FirstName, + LastName: user.LastName, + Followed: followed, } +} - return pbRoutines +func UsersToPB(users orm.UserSlice) []*apiv1.User { + return mapSlice(users, func(user *orm.User) *apiv1.User { + return UserToPB(user, false) + }) } -func parseRoutineToPB(routine *orm.Routine) *apiv1.Routine { +func RoutineToPB(routine *orm.Routine) *apiv1.Routine { + if routine == nil { + return nil + } + var exercises []*apiv1.Exercise - if routine.R != nil && routine.R.Exercises != nil { - exercises = parseExerciseSliceToPB(routine.R.Exercises) + if routine.R != nil { + exercises = ExercisesToPB(routine.R.Exercises) } return &apiv1.Routine{ @@ -51,21 +65,11 @@ func parseRoutineToPB(routine *orm.Routine) *apiv1.Routine { } } -func parseWorkoutSliceToPB(workoutSlice orm.WorkoutSlice, exerciseSlice orm.ExerciseSlice, userSlice orm.UserSlice, mapPersonalBests map[string]struct{}) ([]*apiv1.Workout, error) { - workouts := make([]*apiv1.Workout, 0, len(workoutSlice)) - for _, workout := range workoutSlice { - w, err := parseWorkoutToPB(workout, exerciseSlice, userSlice, mapPersonalBests) - if err != nil { - return nil, fmt.Errorf("failed to parse workout: %w", err) - } - - workouts = append(workouts, w) - } - - return workouts, nil +func RoutinesToPB(routines orm.RoutineSlice) []*apiv1.Routine { + return mapSlice(routines, RoutineToPB) } -func parseWorkoutToPB(workout *orm.Workout, exercises orm.ExerciseSlice, commentUsers orm.UserSlice, mapPersonalBests map[string]struct{}) (*apiv1.Workout, error) { +func WorkoutToPB(workout *orm.Workout, exercises orm.ExerciseSlice, users orm.UserSlice, mapPersonalBests map[string]struct{}) (*apiv1.Workout, error) { var exerciseOrder []string mapExerciseSets := make(map[string][]*apiv1.Set) @@ -75,7 +79,7 @@ func parseWorkoutToPB(workout *orm.Workout, exercises orm.ExerciseSlice, comment exerciseOrder = append(exerciseOrder, set.ExerciseID) } - s, err := parseSetToPB(set, mapPersonalBests) + s, err := setToPB(set, mapPersonalBests) if err != nil { return nil, fmt.Errorf("failed to parse set: %w", err) } @@ -86,7 +90,7 @@ func parseWorkoutToPB(workout *orm.Workout, exercises orm.ExerciseSlice, comment mapExercises := make(map[string]*apiv1.Exercise, len(exercises)) for _, exercise := range exercises { - mapExercises[exercise.ID] = parseExerciseToPB(exercise) + mapExercises[exercise.ID] = ExerciseToPB(exercise) } exerciseSets := make([]*apiv1.ExerciseSets, 0, len(exerciseOrder)) @@ -100,82 +104,63 @@ func parseWorkoutToPB(workout *orm.Workout, exercises orm.ExerciseSlice, comment return &apiv1.Workout{ Id: workout.ID, Name: workout.Name, - User: parseUserToPB(workout.R.User, false), + User: UserToPB(workout.R.User, false), ExerciseSets: exerciseSets, - Comments: parseWorkoutCommentSliceToPB(workout.R.WorkoutComments, commentUsers), + Comments: workoutCommentsToPB(workout.R.WorkoutComments, users), StartedAt: timestamppb.New(workout.StartedAt), FinishedAt: timestamppb.New(workout.FinishedAt), }, nil } -func parseWorkoutCommentSliceToPB(commentSlice orm.WorkoutCommentSlice, users orm.UserSlice) []*apiv1.WorkoutComment { - mapUsers := make(map[string]*orm.User, len(users)) - for _, user := range users { - mapUsers[user.ID] = user - } +func WorkoutsToPB(workouts orm.WorkoutSlice, exercises orm.ExerciseSlice, users orm.UserSlice, mapPersonalBests map[string]struct{}) ([]*apiv1.Workout, error) { + wSlice := make([]*apiv1.Workout, 0, len(workouts)) + for _, workout := range workouts { + w, err := WorkoutToPB(workout, exercises, users, mapPersonalBests) + if err != nil { + return nil, fmt.Errorf("failed to parse workout: %w", err) + } - comments := make([]*apiv1.WorkoutComment, 0, len(commentSlice)) - for _, comment := range commentSlice { - comments = append(comments, parseWorkoutCommentToPB(comment, mapUsers[comment.UserID])) + wSlice = append(wSlice, w) } - return comments + return wSlice, nil } -func parseWorkoutCommentToPB(comment *orm.WorkoutComment, user *orm.User) *apiv1.WorkoutComment { +func WorkoutCommentToPB(comment *orm.WorkoutComment, user *orm.User) *apiv1.WorkoutComment { + if comment == nil { + return nil + } + return &apiv1.WorkoutComment{ Id: comment.ID, - User: parseUserToPB(user, false), + User: UserToPB(user, false), Comment: comment.Comment, CreatedAt: timestamppb.New(comment.CreatedAt), } } -func parseUserToPB(user *orm.User, followed bool) *apiv1.User { - var email string - if user.R != nil && user.R.Auth != nil { - email = user.R.Auth.Email +func workoutCommentsToPB(comments orm.WorkoutCommentSlice, users orm.UserSlice) []*apiv1.WorkoutComment { + mapUsers := make(map[string]*orm.User, len(users)) + for _, user := range users { + mapUsers[user.ID] = user } - return &apiv1.User{ - Id: user.ID, - Email: email, - FirstName: user.FirstName, - LastName: user.LastName, - Followed: followed, + cSlice := make([]*apiv1.WorkoutComment, 0, len(comments)) + for _, comment := range comments { + cSlice = append(cSlice, WorkoutCommentToPB(comment, mapUsers[comment.UserID])) } -} -func parseExerciseSetsFromPB(exerciseSetSlice []*apiv1.ExerciseSets) []repo.ExerciseSet { - exerciseSets := make([]repo.ExerciseSet, 0, len(exerciseSetSlice)) - for _, exerciseSet := range exerciseSetSlice { - sets := make([]repo.Set, 0, len(exerciseSet.GetSets())) - for _, set := range exerciseSet.GetSets() { - sets = append(sets, repo.Set{ - Reps: int(set.GetReps()), - Weight: set.GetWeight(), - }) - } - - exerciseSets = append(exerciseSets, repo.ExerciseSet{ - ExerciseID: exerciseSet.GetExercise().GetId(), - Sets: sets, - }) - } - - return exerciseSets + return cSlice } -var errExerciseNotFound = fmt.Errorf("exercise not found") - -func parseSetSliceToExerciseSetsPB(setSlice orm.SetSlice, exerciseSlice orm.ExerciseSlice) ([]*apiv1.ExerciseSets, error) { - mapExercises := make(map[string]*apiv1.Exercise, len(exerciseSlice)) - for _, exercise := range exerciseSlice { - mapExercises[exercise.ID] = parseExerciseToPB(exercise) +func ExerciseSetSlicesToPB(exercises orm.ExerciseSlice, sets orm.SetSlice) ([]*apiv1.ExerciseSets, error) { + mapExercises := make(map[string]*apiv1.Exercise, len(exercises)) + for _, exercise := range exercises { + mapExercises[exercise.ID] = ExerciseToPB(exercise) } mapExerciseSets := make(map[*apiv1.Exercise][]*apiv1.Set) - for _, set := range setSlice { + for _, set := range sets { exerciseKey, ok := mapExercises[set.ExerciseID] if !ok { return nil, fmt.Errorf("%w: %s", errExerciseNotFound, set.ExerciseID) @@ -185,7 +170,7 @@ func parseSetSliceToExerciseSetsPB(setSlice orm.SetSlice, exerciseSlice orm.Exer mapExerciseSets[exerciseKey] = make([]*apiv1.Set, 0) } - s, err := parseSetToPB(set, nil) + s, err := setToPB(set, nil) if err != nil { return nil, fmt.Errorf("failed to parse set: %w", err) } @@ -204,7 +189,7 @@ func parseSetSliceToExerciseSetsPB(setSlice orm.SetSlice, exerciseSlice orm.Exer return exerciseSets, nil } -func parseExerciseSetSlicesToPB(exercises orm.ExerciseSlice, sets orm.SetSlice) ([]*apiv1.ExerciseSet, error) { +func ExerciseSetSliceToPB(exercises orm.ExerciseSlice, sets orm.SetSlice) ([]*apiv1.ExerciseSet, error) { mapExercises := make(map[string]*orm.Exercise, len(exercises)) for _, exercise := range exercises { mapExercises[exercise.ID] = exercise @@ -212,39 +197,20 @@ func parseExerciseSetSlicesToPB(exercises orm.ExerciseSlice, sets orm.SetSlice) exerciseSets := make([]*apiv1.ExerciseSet, 0, len(sets)) for _, pb := range sets { - set, err := parseSetToPB(pb, nil) + set, err := setToPB(pb, nil) if err != nil { return nil, fmt.Errorf("failed to parse set: %w", err) } exerciseSets = append(exerciseSets, &apiv1.ExerciseSet{ - Exercise: parseExerciseToPB(mapExercises[pb.ExerciseID]), + Exercise: ExerciseToPB(mapExercises[pb.ExerciseID]), Set: set, }) } - return exerciseSets, nil } -func parseUserSliceToPB(users orm.UserSlice) []*apiv1.User { - pbUsers := make([]*apiv1.User, 0, len(users)) - for _, u := range users { - pbUsers = append(pbUsers, &apiv1.User{ - Id: u.ID, - FirstName: u.FirstName, - LastName: u.LastName, - }) - } - - return pbUsers -} - -func parseNotificationSliceToPB( - notifications orm.NotificationSlice, - payload map[string]repo.NotificationPayload, - users orm.UserSlice, - workouts orm.WorkoutSlice, -) []*apiv1.Notification { +func NotificationsToPB(notifications orm.NotificationSlice, payload map[string]repo.NotificationPayload, users orm.UserSlice, workouts orm.WorkoutSlice) []*apiv1.Notification { mapWorkouts := make(map[string]*orm.Workout) for _, w := range workouts { mapWorkouts[w.ID] = w @@ -255,25 +221,44 @@ func parseNotificationSliceToPB( mapUsers[u.ID] = u } - var slice []*apiv1.Notification //nolint:prealloc + nSlice := make([]*apiv1.Notification, 0, len(notifications)) for _, n := range notifications { p, ok := payload[n.ID] if !ok { continue } - notification := parseNotificationToPB(n, mapUsers[p.ActorID], mapWorkouts[p.WorkoutID]) + notification := notificationToPB(n, mapUsers[p.ActorID], mapWorkouts[p.WorkoutID]) if notification == nil { continue } - slice = append(slice, notification) + nSlice = append(nSlice, notification) + } + return nSlice +} + +func ExercisesFromPB(exerciseSets []*apiv1.ExerciseSets) []repo.ExerciseSet { + slice := make([]repo.ExerciseSet, 0, len(exerciseSets)) + for _, exerciseSet := range exerciseSets { + sets := make([]repo.Set, 0, len(exerciseSet.GetSets())) + for _, set := range exerciseSet.GetSets() { + sets = append(sets, repo.Set{ + Reps: int(set.GetReps()), + Weight: set.GetWeight(), + }) + } + + slice = append(slice, repo.ExerciseSet{ + ExerciseID: exerciseSet.GetExercise().GetId(), + Sets: sets, + }) } return slice } -func parseNotificationToPB(n *orm.Notification, u *orm.User, w *orm.Workout) *apiv1.Notification { +func notificationToPB(n *orm.Notification, u *orm.User, w *orm.Workout) *apiv1.Notification { switch n.Type { case orm.NotificationTypeFollow: return &apiv1.Notification{ @@ -281,13 +266,12 @@ func parseNotificationToPB(n *orm.Notification, u *orm.User, w *orm.Workout) *ap NotifiedAtUnix: n.CreatedAt.Unix(), Type: &apiv1.Notification_UserFollowed_{ UserFollowed: &apiv1.Notification_UserFollowed{ - Actor: parseUserToPB(u, false), + Actor: UserToPB(u, false), }, }, } case orm.NotificationTypeWorkoutComment: if w == nil { - // The workout has been deleted. return nil } return &apiv1.Notification{ @@ -295,11 +279,11 @@ func parseNotificationToPB(n *orm.Notification, u *orm.User, w *orm.Workout) *ap NotifiedAtUnix: n.CreatedAt.Unix(), Type: &apiv1.Notification_WorkoutComment_{ WorkoutComment: &apiv1.Notification_WorkoutComment{ - Actor: parseUserToPB(u, false), + Actor: UserToPB(u, false), Workout: &apiv1.Workout{ Id: w.ID, Name: w.Name, - User: parseUserToPB(w.R.User, false), + User: UserToPB(w.R.User, false), }, }, }, @@ -309,50 +293,68 @@ func parseNotificationToPB(n *orm.Notification, u *orm.User, w *orm.Workout) *ap } } -func parseFeedItemsToPB(workouts orm.WorkoutSlice, exercises orm.ExerciseSlice, mapPersonalBests map[string]struct{}) ([]*apiv1.FeedItem, error) { +func FeedItemsToPB(workouts orm.WorkoutSlice, exercises orm.ExerciseSlice, mapPersonalBests map[string]struct{}) ([]*apiv1.FeedItem, error) { items := make([]*apiv1.FeedItem, 0, len(workouts)) for _, workout := range workouts { - w, err := parseWorkoutToPB(workout, exercises, nil, mapPersonalBests) + parsedWorkout, err := WorkoutToPB(workout, exercises, nil, mapPersonalBests) if err != nil { return nil, fmt.Errorf("failed to parse workout: %w", err) } items = append(items, &apiv1.FeedItem{ Type: &apiv1.FeedItem_Workout{ - Workout: w, + Workout: parsedWorkout, }, }) } return items, nil } -func parseSetSliceToPB(setSlice orm.SetSlice, mapPersonalBests map[string]struct{}) ([]*apiv1.Set, error) { - sets := make([]*apiv1.Set, 0, len(setSlice)) - for _, set := range setSlice { - s, err := parseSetToPB(set, mapPersonalBests) +func SetsToPB(sets orm.SetSlice, mapPersonalBests map[string]struct{}) ([]*apiv1.Set, error) { + sSlice := make([]*apiv1.Set, 0, len(sets)) + for _, set := range sets { + s, err := setToPB(set, mapPersonalBests) if err != nil { return nil, fmt.Errorf("failed to parse set: %w", err) } - sets = append(sets, s) + sSlice = append(sSlice, s) } - return sets, nil + return sSlice, nil } -func parseSetToPB(set *orm.Set, mapPersonalBests map[string]struct{}) (*apiv1.Set, error) { +func setToPB(set *orm.Set, mapPersonalBests map[string]struct{}) (*apiv1.Set, error) { reps, err := safe.IntToInt32(set.Reps) if err != nil { return nil, fmt.Errorf("failed to parse reps: %w", err) } - _, personalBest := mapPersonalBests[set.ID] - return &apiv1.Set{ Weight: set.Weight, Reps: reps, Metadata: &apiv1.MetadataSet{ - WorkoutId: set.WorkoutID, - CreatedAt: timestamppb.New(set.CreatedAt), - PersonalBest: personalBest, + WorkoutId: set.WorkoutID, + CreatedAt: timestamppb.New(set.CreatedAt), + PersonalBest: func() bool { + _, personalBest := mapPersonalBests[set.ID] + return personalBest + }(), }, }, nil } + +func mapSlice[Input any, Output any](input []Input, f func(Input) Output) []Output { + output := make([]Output, len(input)) + for i, item := range input { + output[i] = f(item) + } + return output +} + +func safeGetEmail(user *orm.User) string { + if user.R != nil && user.R.Auth != nil { + return user.R.Auth.Email + } + return "" +} + +var errExerciseNotFound = fmt.Errorf("exercise not found")