From bded64757ac0891d1a3d7081ca59c1c4f9e22fab Mon Sep 17 00:00:00 2001 From: Jean Jacques Avril Date: Sat, 4 Jan 2025 11:10:33 +0000 Subject: [PATCH] refactor functional styple in data sources --- .../internal/domain/app_error/app_error.go | 2 +- .../internal/infrastructure/data/helper.go | 36 ++-- .../data/prisma_project_data_source.go | 144 ++++++--------- .../data/prisma_project_task_data_source.go | 140 ++++++-------- .../data/prisma_time_entries_data_source.go | 173 +++++++----------- .../data/prisma_user_data_source.go | 140 ++++++-------- 6 files changed, 259 insertions(+), 376 deletions(-) diff --git a/backend-go/internal/domain/app_error/app_error.go b/backend-go/internal/domain/app_error/app_error.go index 6c36bcd..d4d951a 100644 --- a/backend-go/internal/domain/app_error/app_error.go +++ b/backend-go/internal/domain/app_error/app_error.go @@ -37,4 +37,4 @@ func New(code ErrorCode, message string, status int) *AppError { Message: message, Status: status, } -} +} \ No newline at end of file diff --git a/backend-go/internal/infrastructure/data/helper.go b/backend-go/internal/infrastructure/data/helper.go index 735aa9c..eb3e804 100644 --- a/backend-go/internal/infrastructure/data/helper.go +++ b/backend-go/internal/infrastructure/data/helper.go @@ -6,33 +6,33 @@ import ( "crypto/sha256" "encoding/hex" "errors" + "fmt" E "github.com/IBM/fp-go/either" - O "github.com/IBM/fp-go/option" ) -// Dereference safely dereferences a pointer within an Option. -// If the Option is None, it returns None. Otherwise, it dereferences the pointer and wraps the value. -func Dereference[T any](opt O.Option[*T]) O.Option[T] { - return O.Map(func(ptr *T) T { - return *ptr - })(opt) +func TryDereference[T any](ptr *T) E.Either[error, T] { + if ptr == nil { + return E.Left[T](error(app_error.NewInternalError(fmt.Errorf("Could not dereference")))) + } + return E.Right[error](*ptr) + } -// FromOptionWithError wraps E.FromOption and simplifies error creation. -func FromOptionWithError[A any](err error) func(O.Option[A]) E.Either[error, A] { - return func(opt O.Option[A]) E.Either[error, A] { - return E.FromOption[A](func() error { return err })(opt) - } -} - -func handleDBError(err error, notFoundMessage string) error { - if errors.Is(err, db.ErrNotFound) { - return app_error.NewNotFoundError(notFoundMessage) - } +func WrapAppError(err error) error { return app_error.NewInternalError(err) } +func WrapDBError(notFoundMessage string) func(error) error { + return func(err error) error { + if errors.Is(err, db.ErrNotFound) { + return app_error.NewNotFoundError(notFoundMessage) + } + return app_error.NewInternalError(err) + + } +} + func NullableField[T any](getter func() (T, bool)) *T { if value, ok := getter(); ok { return &value diff --git a/backend-go/internal/infrastructure/data/prisma_project_data_source.go b/backend-go/internal/infrastructure/data/prisma_project_data_source.go index 6e85fff..9f4e052 100644 --- a/backend-go/internal/infrastructure/data/prisma_project_data_source.go +++ b/backend-go/internal/infrastructure/data/prisma_project_data_source.go @@ -1,7 +1,6 @@ package data import ( - "actatempus_backend/internal/domain/app_error" "actatempus_backend/internal/domain/entities" "actatempus_backend/internal/infrastructure/data/db" "context" @@ -10,7 +9,6 @@ import ( A "github.com/IBM/fp-go/array" E "github.com/IBM/fp-go/either" F "github.com/IBM/fp-go/function" - O "github.com/IBM/fp-go/option" ) type PrismaProjectDataSource struct { @@ -23,116 +21,90 @@ func NewPrismaProjectDataSource(client *db.PrismaClient) *PrismaProjectDataSourc // Create a new project func (ds *PrismaProjectDataSource) Create(ctx context.Context, project entities.ProjectCreate) E.Either[error, entities.Project] { - createdProject, err := ds.client.ProjectDbo.CreateOne( - db.ProjectDbo.Name.Set(project.Name), - db.ProjectDbo.User.Link( - db.UserDbo.ID.Equals(project.UserID), + return F.Flow4( + E.Eitherize1(ds.client.ProjectDbo.CreateOne( + db.ProjectDbo.Name.Set(project.Name), + db.ProjectDbo.User.Link( + db.UserDbo.ID.Equals(project.UserID), + ), + db.ProjectDbo.Description.SetIfPresent(project.Description), + db.ProjectDbo.ClientID.SetIfPresent(project.ClientID), + ).Exec), + E.MapLeft[*db.ProjectDboModel](WrapDBError("Could not create project")), + E.Chain( + TryDereference[db.ProjectDboModel], ), - db.ProjectDbo.Description.SetIfPresent(project.Description), - db.ProjectDbo.ClientID.SetIfPresent(project.ClientID), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.Project, error](app_error.NewInternalError(err)) - } - - return F.Pipe3( - O.FromNillable(createdProject), - Dereference, - FromOptionWithError[db.ProjectDboModel](app_error.NewInternalError(fmt.Errorf("Could not create project"))), E.Map[error](mapPrismaProjectToDomain), - ) - + )(ctx) } // FindByID retrieves a project by ID func (ds *PrismaProjectDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.Project] { - project, err := ds.client.ProjectDbo.FindUnique( - db.ProjectDbo.ID.Equals(id), - ).Exec(ctx) - if err != nil { - return E.Left[entities.Project](handleDBError(err, fmt.Sprintf("Project with ID %s not found", id))) - } - - return F.Pipe3( - O.FromNillable(project), - Dereference, - FromOptionWithError[db.ProjectDboModel](app_error.NewNotFoundError(fmt.Sprintf("Project with ID %s not found", id))), + return F.Flow4( + E.Eitherize1(ds.client.ProjectDbo.FindUnique( + db.ProjectDbo.ID.Equals(id), + ).Exec), + E.MapLeft[*db.ProjectDboModel](WrapDBError(fmt.Sprintf("Could not retrieve project with ID %s", id))), + E.Chain( + TryDereference[db.ProjectDboModel], + ), E.Map[error](mapPrismaProjectToDomain), - ) + )(ctx) } // Update updates a project func (ds *PrismaProjectDataSource) Update(ctx context.Context, project entities.ProjectUpdate) E.Either[error, entities.Project] { - - updatedProject, err := ds.client.ProjectDbo.FindUnique( - db.ProjectDbo.ID.Equals(project.ID), - ).Update( - db.ProjectDbo.Name.SetIfPresent(project.Name), - db.ProjectDbo.Description.SetIfPresent(project.Description), - db.ProjectDbo.ClientID.SetIfPresent(project.ClientID), - db.ProjectDbo.User.Link( - db.UserDbo.ID.EqualsIfPresent(project.UserID), + return F.Flow4( + E.Eitherize1(ds.client.ProjectDbo.FindUnique( + db.ProjectDbo.ID.Equals(project.ID), + ).Update( + db.ProjectDbo.Name.SetIfPresent(project.Name), + db.ProjectDbo.Description.SetIfPresent(project.Description), + db.ProjectDbo.ClientID.SetIfPresent(project.ClientID), + db.ProjectDbo.User.Link( + db.UserDbo.ID.EqualsIfPresent(project.UserID), + ), + ).Exec), + E.MapLeft[*db.ProjectDboModel](WrapDBError(fmt.Sprintf("Could not update project with ID %s", project.ID))), + E.Chain( + TryDereference[db.ProjectDboModel], ), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.Project](handleDBError(err, fmt.Sprintf("Could not update project with ID %s", project.ID))) - } - - return F.Pipe3( - O.FromNillable(updatedProject), - Dereference, - FromOptionWithError[db.ProjectDboModel](app_error.NewNotFoundError(fmt.Sprintf("Project with ID %s not found", project.ID))), E.Map[error](mapPrismaProjectToDomain), - ) + )(ctx) } // Delete removes a project func (ds *PrismaProjectDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.Project] { - deleted, err := ds.client.ProjectDbo.FindUnique( - db.ProjectDbo.ID.Equals(id), - ).Delete().Exec(ctx) - if err != nil { - return E.Left[entities.Project](handleDBError(err, fmt.Sprintf("Could not delete project with ID %s", id))) - } - - return F.Pipe3( - O.FromNillable(deleted), - Dereference, - FromOptionWithError[db.ProjectDboModel](app_error.NewNotFoundError(fmt.Sprintf("Project with ID %s not found", id))), + return F.Flow4( + E.Eitherize1(ds.client.ProjectDbo.FindUnique( + db.ProjectDbo.ID.Equals(id), + ).Delete().Exec), + E.MapLeft[*db.ProjectDboModel](WrapDBError(fmt.Sprintf("Could not delete project with ID %s", id))), + E.Chain( + TryDereference[db.ProjectDboModel], + ), E.Map[error](mapPrismaProjectToDomain), - ) + )(ctx) } // FindAll retrieves all projects func (ds *PrismaProjectDataSource) FindAll(ctx context.Context) E.Either[error, []entities.Project] { - projects, err := ds.client.ProjectDbo.FindMany().Exec(ctx) - if err != nil { - return E.Left[[]entities.Project](handleDBError(err, "Could not retrieve projects")) - } - - return F.Pipe2( - projects, - A.Map(mapPrismaProjectToDomain), - E.Right[error], - ) + return F.Flow3( + E.Eitherize1(ds.client.ProjectDbo.FindMany().Exec), + E.MapLeft[[]db.ProjectDboModel](WrapAppError), + E.Map[error](A.Map(mapPrismaProjectToDomain)), + )(ctx) } // FindByUserID retrieves all projects for a user func (ds *PrismaProjectDataSource) FindByUserID(ctx context.Context, userID string) E.Either[error, []entities.Project] { - projects, err := ds.client.ProjectDbo.FindMany( - db.ProjectDbo.UserID.Equals(userID), - ).Exec(ctx) - if err != nil { - return E.Left[[]entities.Project](handleDBError(err, fmt.Sprintf("Could not retrieve projects for user with ID %s", userID))) - } - - return F.Pipe2( - projects, - A.Map(mapPrismaProjectToDomain), - E.Right[error], - ) + return F.Flow3( + E.Eitherize1(ds.client.ProjectDbo.FindMany( + db.ProjectDbo.UserID.Equals(userID), + ).Exec), + E.MapLeft[[]db.ProjectDboModel](WrapDBError(fmt.Sprintf("Could not retrieve projects for user with ID %s", userID))), + E.Map[error](A.Map(mapPrismaProjectToDomain)), + )(ctx) } diff --git a/backend-go/internal/infrastructure/data/prisma_project_task_data_source.go b/backend-go/internal/infrastructure/data/prisma_project_task_data_source.go index 41ea922..eba22ed 100644 --- a/backend-go/internal/infrastructure/data/prisma_project_task_data_source.go +++ b/backend-go/internal/infrastructure/data/prisma_project_task_data_source.go @@ -1,7 +1,6 @@ package data import ( - "actatempus_backend/internal/domain/app_error" "actatempus_backend/internal/domain/entities" "actatempus_backend/internal/infrastructure/data/db" "context" @@ -10,7 +9,6 @@ import ( A "github.com/IBM/fp-go/array" E "github.com/IBM/fp-go/either" F "github.com/IBM/fp-go/function" - O "github.com/IBM/fp-go/option" ) type PrismaProjectTaskDataSource struct { @@ -23,112 +21,86 @@ func NewPrismaProjectTaskDataSource(client *db.PrismaClient) *PrismaProjectTaskD // Create a new ProjectTask func (ds *PrismaProjectTaskDataSource) Create(ctx context.Context, task entities.ProjectTaskCreate) E.Either[error, entities.ProjectTask] { - createdTask, err := ds.client.ProjectTaskDbo.CreateOne( - db.ProjectTaskDbo.Name.Set(task.Name), - db.ProjectTaskDbo.Project.Link( - db.ProjectDbo.ID.Equals(task.ProjectID), + return F.Flow4( + E.Eitherize1(ds.client.ProjectTaskDbo.CreateOne( + db.ProjectTaskDbo.Name.Set(task.Name), + db.ProjectTaskDbo.Project.Link( + db.ProjectDbo.ID.Equals(task.ProjectID), + ), + db.ProjectTaskDbo.Description.SetIfPresent(task.Description), + ).Exec), + E.MapLeft[*db.ProjectTaskDboModel](WrapDBError("Could not create project task")), + E.Chain( + TryDereference[db.ProjectTaskDboModel], ), - db.ProjectTaskDbo.Description.SetIfPresent(task.Description), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.ProjectTask, error](handleDBError(err, fmt.Sprintf("Could not create project task"))) - } - - return F.Pipe3( - O.FromNillable(createdTask), - Dereference, - FromOptionWithError[db.ProjectTaskDboModel](app_error.NewInternalError(fmt.Errorf("Could not create project task"))), E.Map[error](mapPrismaProjectTaskToDomain), - ) + )(ctx) } // Find ProjectTask by ID func (ds *PrismaProjectTaskDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.ProjectTask] { - task, err := ds.client.ProjectTaskDbo.FindUnique( - db.ProjectTaskDbo.ID.Equals(id), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.ProjectTask](handleDBError(err, fmt.Sprintf("ProjectTask with ID %s not found", id))) - } - - return F.Pipe3( - O.FromNillable(task), - Dereference, - FromOptionWithError[db.ProjectTaskDboModel](app_error.NewNotFoundError(fmt.Sprintf("ProjectTask with ID %s not found", id))), + return F.Flow4( + E.Eitherize1(ds.client.ProjectTaskDbo.FindUnique( + db.ProjectTaskDbo.ID.Equals(id), + ).Exec), + E.MapLeft[*db.ProjectTaskDboModel](WrapDBError(fmt.Sprintf("Could not retrieve project task with ID %s", id))), + E.Chain( + TryDereference[db.ProjectTaskDboModel], + ), E.Map[error](mapPrismaProjectTaskToDomain), - ) + )(ctx) } // Update an existing ProjectTask func (ds *PrismaProjectTaskDataSource) Update(ctx context.Context, task entities.ProjectTaskUpdate) E.Either[error, entities.ProjectTask] { - updatedTask, err := ds.client.ProjectTaskDbo.FindUnique( - db.ProjectTaskDbo.ID.Equals(task.ID), - ).Update( - db.ProjectTaskDbo.Name.SetIfPresent(task.Name), - db.ProjectTaskDbo.Description.SetIfPresent(task.Description), - db.ProjectTaskDbo.Project.Link( - db.ProjectDbo.ID.EqualsIfPresent(task.ProjectID), + return F.Flow4( + E.Eitherize1(ds.client.ProjectTaskDbo.FindUnique( + db.ProjectTaskDbo.ID.Equals(task.ID), + ).Update( + db.ProjectTaskDbo.Name.SetIfPresent(task.Name), + db.ProjectTaskDbo.Description.SetIfPresent(task.Description), + db.ProjectTaskDbo.Project.Link( + db.ProjectDbo.ID.EqualsIfPresent(task.ProjectID), + ), + ).Exec), + E.MapLeft[*db.ProjectTaskDboModel](WrapDBError(fmt.Sprintf("Could not update project task with ID %s", task.ID))), + E.Chain( + TryDereference[db.ProjectTaskDboModel], ), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.ProjectTask](handleDBError(err, fmt.Sprintf("Could not update project task with ID %s", task.ID))) - } - - return F.Pipe3( - O.FromNillable(updatedTask), - Dereference, - FromOptionWithError[db.ProjectTaskDboModel](app_error.NewNotFoundError(fmt.Sprintf("ProjectTask with ID %s not found", task.ID))), E.Map[error](mapPrismaProjectTaskToDomain), - ) + )(ctx) } // Delete a ProjectTask func (ds *PrismaProjectTaskDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.ProjectTask] { - deletedTask, err := ds.client.ProjectTaskDbo.FindUnique( - db.ProjectTaskDbo.ID.Equals(id), - ).Delete().Exec(ctx) - - if err != nil { - return E.Left[entities.ProjectTask](handleDBError(err, fmt.Sprintf("Could not delete project task with ID %s", id))) - } - - return F.Pipe3( - O.FromNillable(deletedTask), - Dereference, - FromOptionWithError[db.ProjectTaskDboModel](app_error.NewNotFoundError(fmt.Sprintf("ProjectTask with ID %s not found", id))), + return F.Flow4( + E.Eitherize1(ds.client.ProjectTaskDbo.FindUnique( + db.ProjectTaskDbo.ID.Equals(id), + ).Delete().Exec), + E.MapLeft[*db.ProjectTaskDboModel](WrapDBError(fmt.Sprintf("Could not delete project task with ID %s", id))), + E.Chain( + TryDereference[db.ProjectTaskDboModel], + ), E.Map[error](mapPrismaProjectTaskToDomain), - ) + )(ctx) } // FindAll retrieves all ProjectTasks func (ds *PrismaProjectTaskDataSource) FindAll(ctx context.Context) E.Either[error, []entities.ProjectTask] { - tasks, err := ds.client.ProjectTaskDbo.FindMany().Exec(ctx) - if err != nil { - return E.Left[[]entities.ProjectTask](handleDBError(err, "Could not retrieve project tasks")) - } - - return F.Pipe2( - tasks, - A.Map(mapPrismaProjectTaskToDomain), - E.Right[error], - ) + return F.Flow3( + E.Eitherize1(ds.client.ProjectTaskDbo.FindMany().Exec), + E.MapLeft[[]db.ProjectTaskDboModel](WrapDBError("Could not retrieve project tasks")), + E.Map[error](A.Map(mapPrismaProjectTaskToDomain)), + )(ctx) } // FindByProjectID retrieves all ProjectTasks for a given Project func (ds *PrismaProjectTaskDataSource) FindByProjectID(ctx context.Context, projectID string) E.Either[error, []entities.ProjectTask] { - tasks, err := ds.client.ProjectTaskDbo.FindMany( - db.ProjectTaskDbo.ProjectID.Equals(projectID), - ).Exec(ctx) - if err != nil { - return E.Left[[]entities.ProjectTask](handleDBError(err, fmt.Sprintf("Could not retrieve project tasks for project with ID %s", projectID))) - } - - return F.Pipe2( - tasks, - A.Map(mapPrismaProjectTaskToDomain), - E.Right[error], - ) + return F.Flow3( + E.Eitherize1(ds.client.ProjectTaskDbo.FindMany( + db.ProjectTaskDbo.ProjectID.Equals(projectID), + ).Exec), + E.MapLeft[[]db.ProjectTaskDboModel](WrapDBError(fmt.Sprintf("Could not retrieve project tasks for project with ID %s", projectID))), + E.Map[error](A.Map(mapPrismaProjectTaskToDomain)), + )(ctx) } diff --git a/backend-go/internal/infrastructure/data/prisma_time_entries_data_source.go b/backend-go/internal/infrastructure/data/prisma_time_entries_data_source.go index 12820e1..04dc44d 100644 --- a/backend-go/internal/infrastructure/data/prisma_time_entries_data_source.go +++ b/backend-go/internal/infrastructure/data/prisma_time_entries_data_source.go @@ -1,7 +1,6 @@ package data import ( - "actatempus_backend/internal/domain/app_error" "actatempus_backend/internal/domain/entities" "actatempus_backend/internal/infrastructure/data/db" "context" @@ -10,7 +9,6 @@ import ( A "github.com/IBM/fp-go/array" E "github.com/IBM/fp-go/either" F "github.com/IBM/fp-go/function" - O "github.com/IBM/fp-go/option" ) type PrismaTimeEntryDataSource struct { @@ -23,135 +21,104 @@ func NewPrismaTimeEntryDataSource(client *db.PrismaClient) *PrismaTimeEntryDataS // Create a new TimeEntry func (ds *PrismaTimeEntryDataSource) Create(ctx context.Context, entry entities.TimeEntryCreate) E.Either[error, entities.TimeEntry] { - createdEntry, err := ds.client.TimeEntryDbo.CreateOne( - db.TimeEntryDbo.StartTime.Set(entry.StartTime), - db.TimeEntryDbo.User.Link( - db.UserDbo.ID.Equals(entry.UserID), - ), db.TimeEntryDbo.Project.Link( - db.ProjectDbo.ID.Equals(entry.ProjectID), + return F.Flow4( + E.Eitherize1(ds.client.TimeEntryDbo.CreateOne( + db.TimeEntryDbo.StartTime.Set(entry.StartTime), + db.TimeEntryDbo.User.Link( + db.UserDbo.ID.Equals(entry.UserID), + ), db.TimeEntryDbo.Project.Link( + db.ProjectDbo.ID.Equals(entry.ProjectID), + ), + db.TimeEntryDbo.EndTime.SetIfPresent(entry.EndTime), + db.TimeEntryDbo.Description.SetIfPresent(entry.Description), + ).Exec), + E.MapLeft[*db.TimeEntryDboModel](WrapDBError("Could not create time entry")), + E.Chain( + TryDereference[db.TimeEntryDboModel], ), - db.TimeEntryDbo.EndTime.SetIfPresent(entry.EndTime), - db.TimeEntryDbo.Description.SetIfPresent(entry.Description), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.TimeEntry, error](app_error.NewInternalError(err)) - } - - return F.Pipe3( - O.FromNillable(createdEntry), - Dereference, - FromOptionWithError[db.TimeEntryDboModel](app_error.NewInternalError(fmt.Errorf("Could not create time entry"))), E.Map[error](mapPrismaTimeEntryToDomain), - ) + )(ctx) } // Find TimeEntry by ID func (ds *PrismaTimeEntryDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.TimeEntry] { - entry, err := ds.client.TimeEntryDbo.FindUnique( - db.TimeEntryDbo.ID.Equals(id), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.TimeEntry](handleDBError(err, fmt.Sprintf("TimeEntry with ID %s not found", id))) - } - - return F.Pipe3( - O.FromNillable(entry), - Dereference, - FromOptionWithError[db.TimeEntryDboModel](app_error.NewNotFoundError(fmt.Sprintf("TimeEntry with ID %s not found", id))), + return F.Flow4( + E.Eitherize1(ds.client.TimeEntryDbo.FindUnique( + db.TimeEntryDbo.ID.Equals(id), + ).Exec), + E.MapLeft[*db.TimeEntryDboModel](WrapDBError(fmt.Sprintf("Could not retrieve time entry with ID %s", id))), + E.Chain( + TryDereference[db.TimeEntryDboModel], + ), E.Map[error](mapPrismaTimeEntryToDomain), - ) + )(ctx) } // Update an existing TimeEntry func (ds *PrismaTimeEntryDataSource) Update(ctx context.Context, entry entities.TimeEntryUpdate) E.Either[error, entities.TimeEntry] { - updatedEntry, err := ds.client.TimeEntryDbo.FindUnique( - db.TimeEntryDbo.ID.Equals(entry.ID), - ).Update( - db.TimeEntryDbo.StartTime.SetIfPresent(entry.StartTime), - db.TimeEntryDbo.EndTime.SetIfPresent(entry.EndTime), - db.TimeEntryDbo.Description.SetIfPresent(entry.Description), - db.TimeEntryDbo.User.Link( - db.UserDbo.ID.EqualsIfPresent(entry.UserID), + return F.Flow4( + E.Eitherize1(ds.client.TimeEntryDbo.FindUnique( + db.TimeEntryDbo.ID.Equals(entry.ID), + ).Update( + db.TimeEntryDbo.StartTime.SetIfPresent(entry.StartTime), + db.TimeEntryDbo.EndTime.SetIfPresent(entry.EndTime), + db.TimeEntryDbo.Description.SetIfPresent(entry.Description), + db.TimeEntryDbo.User.Link( + db.UserDbo.ID.EqualsIfPresent(entry.UserID), + ), + db.TimeEntryDbo.Project.Link( + db.ProjectDbo.ID.EqualsIfPresent(entry.ProjectID), + ), + ).Exec), + E.MapLeft[*db.TimeEntryDboModel](WrapDBError(fmt.Sprintf("Could not update time entry with ID %s", entry.ID))), + E.Chain( + TryDereference[db.TimeEntryDboModel], ), - db.TimeEntryDbo.Project.Link( - db.ProjectDbo.ID.EqualsIfPresent(entry.ProjectID), - ), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.TimeEntry, error](handleDBError(err, fmt.Sprintf("Could not update time entry with ID %s", entry.ID))) - } - - return F.Pipe3( - O.FromNillable(updatedEntry), - Dereference, - FromOptionWithError[db.TimeEntryDboModel](app_error.NewNotFoundError(fmt.Sprintf("TimeEntry with ID %s not found", entry.ID))), E.Map[error](mapPrismaTimeEntryToDomain), - ) + )(ctx) } // Delete a TimeEntry func (ds *PrismaTimeEntryDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.TimeEntry] { - deletedEntry, err := ds.client.TimeEntryDbo.FindUnique( - db.TimeEntryDbo.ID.Equals(id), - ).Delete().Exec(ctx) - - if err != nil { - return E.Left[entities.TimeEntry](handleDBError(err, fmt.Sprintf("Could not delete time entry with ID %s", id))) - } - - return F.Pipe3( - O.FromNillable(deletedEntry), - Dereference, - FromOptionWithError[db.TimeEntryDboModel](app_error.NewNotFoundError(fmt.Sprintf("TimeEntry with ID %s not found", id))), + return F.Flow4( + E.Eitherize1(ds.client.TimeEntryDbo.FindUnique( + db.TimeEntryDbo.ID.Equals(id), + ).Delete().Exec), + E.MapLeft[*db.TimeEntryDboModel](WrapDBError(fmt.Sprintf("Could not delete time entry with ID %s", id))), + E.Chain( + TryDereference[db.TimeEntryDboModel], + ), E.Map[error](mapPrismaTimeEntryToDomain), - ) + )(ctx) } // FindAll retrieves all TimeEntries func (ds *PrismaTimeEntryDataSource) FindAll(ctx context.Context) E.Either[error, []entities.TimeEntry] { - entries, err := ds.client.TimeEntryDbo.FindMany().Exec(ctx) - if err != nil { - return E.Left[[]entities.TimeEntry](handleDBError(err, "Could not retrieve time entries")) - } - - return F.Pipe2( - entries, - A.Map(mapPrismaTimeEntryToDomain), - E.Right[error], - ) + return F.Flow3( + E.Eitherize1(ds.client.TimeEntryDbo.FindMany().Exec), + E.MapLeft[[]db.TimeEntryDboModel](WrapDBError("Could not retrieve time entries")), + E.Map[error](A.Map(mapPrismaTimeEntryToDomain)), + )(ctx) } // FindByUserID retrieves all TimeEntries by UserID func (ds *PrismaTimeEntryDataSource) FindByUserID(ctx context.Context, userID string) E.Either[error, []entities.TimeEntry] { - entries, err := ds.client.TimeEntryDbo.FindMany( - db.TimeEntryDbo.UserID.Equals(userID), - ).Exec(ctx) - if err != nil { - return E.Left[[]entities.TimeEntry](handleDBError(err, fmt.Sprintf("Could not retrieve time entries for user with ID %s", userID))) - } - - return F.Pipe2( - entries, - A.Map(mapPrismaTimeEntryToDomain), - E.Right[error], - ) + return F.Flow3( + E.Eitherize1(ds.client.TimeEntryDbo.FindMany( + db.TimeEntryDbo.UserID.Equals(userID), + ).Exec), + E.MapLeft[[]db.TimeEntryDboModel](WrapDBError(fmt.Sprintf("Could not retrieve time entries for user with ID %s", userID))), + E.Map[error](A.Map(mapPrismaTimeEntryToDomain)), + )(ctx) } // FindByProjectID retrieves all TimeEntries by ProjectID func (ds *PrismaTimeEntryDataSource) FindByProjectID(ctx context.Context, projectID string) E.Either[error, []entities.TimeEntry] { - entries, err := ds.client.TimeEntryDbo.FindMany( - db.TimeEntryDbo.ProjectID.Equals(projectID), - ).Exec(ctx) - if err != nil { - return E.Left[[]entities.TimeEntry](handleDBError(err, fmt.Sprintf("Could not retrieve time entries for project with ID %s", projectID))) - } - - return F.Pipe2( - entries, - A.Map(mapPrismaTimeEntryToDomain), - E.Right[error], - ) + return F.Flow3( + E.Eitherize1(ds.client.TimeEntryDbo.FindMany( + db.TimeEntryDbo.ProjectID.Equals(projectID), + ).Exec), + E.MapLeft[[]db.TimeEntryDboModel](WrapDBError(fmt.Sprintf("Could not retrieve time entries for project with ID %s", projectID))), + E.Map[error](A.Map(mapPrismaTimeEntryToDomain)), + )(ctx) } diff --git a/backend-go/internal/infrastructure/data/prisma_user_data_source.go b/backend-go/internal/infrastructure/data/prisma_user_data_source.go index f6c4d5a..6f0ee4f 100644 --- a/backend-go/internal/infrastructure/data/prisma_user_data_source.go +++ b/backend-go/internal/infrastructure/data/prisma_user_data_source.go @@ -1,7 +1,6 @@ package data import ( - "actatempus_backend/internal/domain/app_error" "actatempus_backend/internal/domain/entities" "actatempus_backend/internal/infrastructure/data/db" "actatempus_backend/internal/utils" @@ -11,7 +10,6 @@ import ( A "github.com/IBM/fp-go/array" E "github.com/IBM/fp-go/either" F "github.com/IBM/fp-go/function" - O "github.com/IBM/fp-go/option" ) type PrismaUserDataSource struct { @@ -23,107 +21,81 @@ func NewPrismaUserDataSource(client *db.PrismaClient) *PrismaUserDataSource { } func (ds *PrismaUserDataSource) Create(ctx context.Context, user entities.UserCreate) E.Either[error, entities.User] { - createdUser, err := ds.client.UserDbo.CreateOne( - db.UserDbo.Name.Set(user.Name), - db.UserDbo.Email.Set(user.Email), - db.UserDbo.Password.Set(GenerateSecureHash(user.Password)), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.User, error](app_error.NewInternalError(err)) - } - - return F.Pipe3( - O.FromNillable(createdUser), - Dereference, - FromOptionWithError[db.UserDboModel](app_error.NewInternalError(fmt.Errorf("Could not create user"))), + return F.Flow4( + E.Eitherize1(ds.client.UserDbo.CreateOne( + db.UserDbo.Name.Set(user.Name), + db.UserDbo.Email.Set(user.Email), + db.UserDbo.Password.Set(GenerateSecureHash(user.Password)), + ).Exec), + E.MapLeft[*db.UserDboModel](WrapDBError("Could not create user")), + E.Chain( + TryDereference[db.UserDboModel], + ), E.Map[error](mapPrismaUserToDomain), - ) + )(ctx) } func (ds *PrismaUserDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.User] { - user, err := ds.client.UserDbo.FindUnique( - db.UserDbo.ID.Equals(id), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.User, error](handleDBError(err, fmt.Sprintf("Query for user with ID %s failed", id))) - } - - return F.Pipe3( - O.FromNillable(user), - Dereference, - FromOptionWithError[db.UserDboModel](app_error.NewNotFoundError(fmt.Sprintf("User with ID %s not found", user.ID))), + return F.Flow4( + E.Eitherize1(ds.client.UserDbo.FindUnique( + db.UserDbo.ID.Equals(id), + ).Exec), + E.MapLeft[*db.UserDboModel](WrapDBError(fmt.Sprintf("Could not retrieve user with ID %s", id))), + E.Chain( + TryDereference[db.UserDboModel], + ), E.Map[error](mapPrismaUserToDomain), - ) + )(ctx) } func (ds *PrismaUserDataSource) FindByEmail(ctx context.Context, email string) E.Either[error, entities.User] { - user, err := ds.client.UserDbo.FindUnique( - db.UserDbo.Email.Equals(email), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.User, error](handleDBError(err, fmt.Sprintf("Query for user with email %s failed", email))) - - } - - return F.Pipe3( - O.FromNillable(user), - Dereference, - FromOptionWithError[db.UserDboModel](app_error.NewNotFoundError(fmt.Sprintf("User with email %s not found", email))), + return F.Flow4( + E.Eitherize1(ds.client.UserDbo.FindUnique( + db.UserDbo.Email.Equals(email), + ).Exec), + E.MapLeft[*db.UserDboModel](WrapDBError(fmt.Sprintf("Could not retrieve user with email %s", email))), + E.Chain( + TryDereference[db.UserDboModel], + ), E.Map[error](mapPrismaUserToDomain), - ) + )(ctx) } func (ds *PrismaUserDataSource) Update(ctx context.Context, user entities.UserUpdate) E.Either[error, entities.User] { - updatedUser, err := ds.client.UserDbo.FindUnique( - db.UserDbo.ID.Equals(user.ID), - ).Update( - db.UserDbo.Name.SetIfPresent(user.Name), - db.UserDbo.Email.SetIfPresent(user.Email), - db.UserDbo.Password.SetIfPresent(utils.Let(user.Password, GenerateSecureHash)), - ).Exec(ctx) - - if err != nil { - return E.Left[entities.User, error](handleDBError(err, fmt.Sprintf("Could not update user with ID %s", user.ID))) - } - - return F.Pipe3( - O.FromNillable(updatedUser), - Dereference, - FromOptionWithError[db.UserDboModel](app_error.NewNotFoundError(fmt.Sprintf("User with ID %s not found", user.ID))), + return F.Flow4( + E.Eitherize1(ds.client.UserDbo.FindUnique( + db.UserDbo.ID.Equals(user.ID), + ).Update( + db.UserDbo.Name.SetIfPresent(user.Name), + db.UserDbo.Email.SetIfPresent(user.Email), + db.UserDbo.Password.SetIfPresent(utils.Let(user.Password, GenerateSecureHash)), + ).Exec), + E.MapLeft[*db.UserDboModel](WrapDBError(fmt.Sprintf("Could not update user with ID %s", user.ID))), + E.Chain( + TryDereference[db.UserDboModel], + ), E.Map[error](mapPrismaUserToDomain), - ) + )(ctx) } func (ds *PrismaUserDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.User] { - deleted, err := ds.client.UserDbo.FindUnique( - db.UserDbo.ID.Equals(id), - ).Delete().Exec(ctx) - - if err != nil { - return E.Left[entities.User, error](handleDBError(err, fmt.Sprintf("Could not delete user with ID %s", id))) - } - - return F.Pipe3( - O.FromNillable(deleted), - Dereference, - FromOptionWithError[db.UserDboModel](app_error.NewNotFoundError(fmt.Sprintf("User with ID %s not found", id))), + return F.Flow4( + E.Eitherize1(ds.client.UserDbo.FindUnique( + db.UserDbo.ID.Equals(id), + ).Delete().Exec), + E.MapLeft[*db.UserDboModel](WrapDBError(fmt.Sprintf("Could not delete user with ID %s", id))), + E.Chain( + TryDereference[db.UserDboModel], + ), E.Map[error](mapPrismaUserToDomain), - ) + )(ctx) } func (ds *PrismaUserDataSource) FindAll(ctx context.Context) E.Either[error, []entities.User] { - users, err := ds.client.UserDbo.FindMany().Exec(ctx) - if err != nil { - return E.Left[[]entities.User, error](handleDBError(err, "Could not retrieve users")) - } - - return F.Pipe2( - users, - A.Map(mapPrismaUserToDomain), - E.Right[error], - ) + return F.Flow3( + E.Eitherize1(ds.client.UserDbo.FindMany().Exec), + E.MapLeft[[]db.UserDboModel](WrapDBError("Could not retrieve users")), + E.Map[error](A.Map(mapPrismaUserToDomain)), + )(ctx) }