implemented data sources with prisma in go
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package dto
|
||||
|
||||
// ProjectDTO represents the data structure for transferring project data.
|
||||
type ProjectDTO struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description *string `json:"description"`
|
||||
ClientID *string `json:"clientId"`
|
||||
UserID string `json:"userId"`
|
||||
CreatedAt string `json:"createdAt"` // Use ISO8601 format
|
||||
UpdatedAt string `json:"updatedAt"` // Use ISO8601 format
|
||||
}
|
||||
|
||||
// ProjectCreateDTO is used for creating a new project.
|
||||
type ProjectCreateDTO struct {
|
||||
Name string `json:"name"`
|
||||
Description *string `json:"description"`
|
||||
ClientID *string `json:"clientId"`
|
||||
UserID string `json:"userId"`
|
||||
}
|
||||
|
||||
// ProjectUpdateDTO is used for updating an existing project.
|
||||
type ProjectUpdateDTO struct {
|
||||
Name *string `json:"name"`
|
||||
Description *string `json:"description"`
|
||||
ClientID *string `json:"clientId"`
|
||||
UserID *string `json:"userId"`
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package dto
|
||||
|
||||
// ProjectTaskDTO represents the data structure for transferring project task data.
|
||||
type ProjectTaskDTO struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description *string `json:"description"`
|
||||
ProjectID string `json:"projectId"`
|
||||
CreatedAt string `json:"createdAt"` // Use ISO8601 format
|
||||
UpdatedAt string `json:"updatedAt"` // Use ISO8601 format
|
||||
}
|
||||
|
||||
// ProjectTaskCreateDTO is used for creating a new project task.
|
||||
type ProjectTaskCreateDTO struct {
|
||||
Name string `json:"name"`
|
||||
Description *string `json:"description"`
|
||||
ProjectID string `json:"projectId"`
|
||||
}
|
||||
|
||||
// ProjectTaskUpdateDTO is used for updating an existing project task.
|
||||
type ProjectTaskUpdateDTO struct {
|
||||
Name *string `json:"name"`
|
||||
Description *string `json:"description"`
|
||||
ProjectID *string `json:"projectId"`
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package dto
|
||||
|
||||
// TimeEntryDTO represents the data structure for transferring time entry data.
|
||||
type TimeEntryDTO struct {
|
||||
ID string `json:"id"`
|
||||
StartTime string `json:"startTime"` // Use ISO8601 format
|
||||
EndTime *string `json:"endTime"` // Use ISO8601 format
|
||||
Description *string `json:"description"`
|
||||
UserID string `json:"userId"`
|
||||
ProjectID string `json:"projectId"`
|
||||
CreatedAt string `json:"createdAt"` // Use ISO8601 format
|
||||
UpdatedAt string `json:"updatedAt"` // Use ISO8601 format
|
||||
}
|
||||
|
||||
// TimeEntryCreateDTO is used for creating a new time entry.
|
||||
type TimeEntryCreateDTO struct {
|
||||
StartTime string `json:"startTime"` // Use ISO8601 format
|
||||
EndTime *string `json:"endTime"` // Use ISO8601 format
|
||||
Description *string `json:"description"`
|
||||
UserID string `json:"userId"`
|
||||
ProjectID string `json:"projectId"`
|
||||
}
|
||||
|
||||
// TimeEntryUpdateDTO is used for updating an existing time entry.
|
||||
type TimeEntryUpdateDTO struct {
|
||||
StartTime *string `json:"startTime"` // Use ISO8601 format
|
||||
EndTime *string `json:"endTime"` // Use ISO8601 format
|
||||
Description *string `json:"description"`
|
||||
UserID *string `json:"userId"`
|
||||
ProjectID *string `json:"projectId"`
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package dto
|
||||
|
||||
// UserDTO represents the data structure for transferring user data.
|
||||
type UserDTO struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Password *string `json:"password,omitempty"` // Optional for cases where password shouldn't be exposed
|
||||
CreatedAt string `json:"createdAt"` // Use ISO8601 format
|
||||
UpdatedAt string `json:"updatedAt"` // Use ISO8601 format
|
||||
}
|
||||
|
||||
// UserCreateDTO is used for creating a new user.
|
||||
type UserCreateDTO struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// UserUpdateDTO is used for updating an existing user.
|
||||
type UserUpdateDTO struct {
|
||||
Name *string `json:"name"`
|
||||
Email *string `json:"email"`
|
||||
Password *string `json:"password"`
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package mappers
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Parses ISO8601 timestamps
|
||||
func parseISOTime(value string) time.Time {
|
||||
parsed, err := time.Parse(time.RFC3339, value)
|
||||
if err != nil {
|
||||
panic("Invalid ISO8601 format") // Or handle error gracefully
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package mappers
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/application/services/dto"
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MapProjectToDTO converts a Project domain object to a ProjectDTO.
|
||||
func MapProjectToDTO(project entities.Project) dto.ProjectDTO {
|
||||
return dto.ProjectDTO{
|
||||
ID: project.ID,
|
||||
Name: project.Name,
|
||||
Description: project.Description,
|
||||
ClientID: project.ClientID,
|
||||
UserID: project.UserID,
|
||||
CreatedAt: project.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: project.UpdatedAt.Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
|
||||
// MapCreateDTOToProject converts a ProjectCreateDTO to a Project domain object.
|
||||
func MapCreateDTOToProject(dto dto.ProjectCreateDTO) entities.ProjectCreate {
|
||||
return entities.ProjectCreate{
|
||||
Name: dto.Name,
|
||||
Description: dto.Description,
|
||||
ClientID: dto.ClientID,
|
||||
UserID: dto.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
// MapUpdateDTOToProject converts a ProjectUpdateDTO to a partial Project domain object.
|
||||
func MapUpdateDTOToProject(dto dto.ProjectUpdateDTO, ID string) entities.ProjectUpdate {
|
||||
return entities.ProjectUpdate{
|
||||
ID: ID,
|
||||
Name: dto.Name,
|
||||
Description: dto.Description,
|
||||
ClientID: dto.ClientID,
|
||||
UserID: dto.UserID,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package mappers
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/application/services/dto"
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MapProjectTaskToDTO converts a ProjectTask domain object to a ProjectTaskDTO.
|
||||
func MapProjectTaskToDTO(task entities.ProjectTask) dto.ProjectTaskDTO {
|
||||
return dto.ProjectTaskDTO{
|
||||
ID: task.ID,
|
||||
Name: task.Name,
|
||||
Description: task.Description,
|
||||
ProjectID: task.ProjectID,
|
||||
CreatedAt: task.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: task.UpdatedAt.Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
|
||||
// MapCreateDTOToProjectTask converts a ProjectTaskCreateDTO to a ProjectTask domain object.
|
||||
func MapCreateDTOToProjectTask(dto dto.ProjectTaskCreateDTO) entities.ProjectTaskCreate {
|
||||
return entities.ProjectTaskCreate{
|
||||
Name: dto.Name,
|
||||
Description: dto.Description,
|
||||
ProjectID: dto.ProjectID,
|
||||
}
|
||||
}
|
||||
|
||||
// MapUpdateDTOToProjectTask converts a ProjectTaskUpdateDTO to a partial ProjectTask domain object.
|
||||
func MapUpdateDTOToProjectTask(dto dto.ProjectTaskUpdateDTO, ID string) entities.ProjectTaskUpdate {
|
||||
return entities.ProjectTaskUpdate{
|
||||
ID: ID,
|
||||
Name: dto.Name,
|
||||
Description: dto.Description,
|
||||
ProjectID: dto.ProjectID,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package mappers
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/application/services/dto"
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"actatempus_backend/internal/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MapTimeEntryToDTO converts a TimeEntry domain object to a TimeEntryDTO.
|
||||
func MapTimeEntryToDTO(entry entities.TimeEntry) dto.TimeEntryDTO {
|
||||
return dto.TimeEntryDTO{
|
||||
ID: entry.ID,
|
||||
StartTime: entry.StartTime.Format(time.RFC3339),
|
||||
EndTime: utils.Let(entry.EndTime, func (t time.Time) string { return t.Format(time.RFC3339);}),
|
||||
Description: entry.Description,
|
||||
UserID: entry.UserID,
|
||||
ProjectID: entry.ProjectID,
|
||||
CreatedAt: entry.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: entry.UpdatedAt.Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
|
||||
// MapCreateDTOToTimeEntry converts a TimeEntryCreateDTO to a TimeEntry domain object.
|
||||
func MapCreateDTOToTimeEntry(dto dto.TimeEntryCreateDTO) entities.TimeEntryCreate {
|
||||
|
||||
return entities.TimeEntryCreate{
|
||||
StartTime: parseISOTime(dto.StartTime),
|
||||
EndTime: utils.Let(dto.EndTime,parseISOTime),
|
||||
Description: dto.Description,
|
||||
UserID: dto.UserID,
|
||||
ProjectID: dto.ProjectID,
|
||||
}
|
||||
}
|
||||
|
||||
// MapUpdateDTOToTimeEntry converts a TimeEntryUpdateDTO to a partial TimeEntry domain object.
|
||||
func MapUpdateDTOToTimeEntry(dto dto.TimeEntryUpdateDTO, ID string) entities.TimeEntryUpdate {
|
||||
return entities.TimeEntryUpdate{
|
||||
ID: ID,
|
||||
StartTime: utils.Let(dto.StartTime,parseISOTime),
|
||||
EndTime: utils.Let(dto.EndTime,parseISOTime),
|
||||
Description: dto.Description,
|
||||
UserID: dto.UserID,
|
||||
ProjectID: dto.ProjectID,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package mappers
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/application/services/dto"
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MapUserToDTO converts a User domain object to a UserDTO.
|
||||
func MapUserToDTO(user entities.User) dto.UserDTO {
|
||||
return dto.UserDTO{
|
||||
ID: user.ID,
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
CreatedAt: user.CreatedAt.Format(time.RFC3339),
|
||||
UpdatedAt: user.UpdatedAt.Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
|
||||
// MapCreateDTOToUser converts a UserCreateDTO to a User domain object.
|
||||
func MapCreateDTOToUser(dto dto.UserCreateDTO) entities.UserCreate {
|
||||
return entities.UserCreate{
|
||||
Name: dto.Name,
|
||||
Email: dto.Email,
|
||||
Password: dto.Password,
|
||||
}
|
||||
}
|
||||
|
||||
// MapUpdateDTOToUser converts a UserUpdateDTO to a partial User domain object.
|
||||
func MapUpdateDTOToUser(dto dto.UserUpdateDTO, ID string) entities.UserUpdate {
|
||||
return entities.UserUpdate{
|
||||
ID: ID,
|
||||
Name: dto.Name,
|
||||
Email: dto.Email,
|
||||
Password: dto.Password,
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package usecases
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"actatempus_backend/internal/domain/repositories"
|
||||
)
|
||||
|
||||
type RegisterUserUseCase struct {
|
||||
userRepository repositories.UserRepository
|
||||
}
|
||||
|
||||
func NewRegisterUserUseCase(userRepo repositories.UserRepository) *RegisterUserUseCase {
|
||||
return &RegisterUserUseCase{userRepository: userRepo}
|
||||
}
|
||||
|
||||
func (uc *RegisterUserUseCase) Execute(name, email, password string) error {
|
||||
user := &entities.User{
|
||||
Name: name,
|
||||
Email: email,
|
||||
Password: password, // In der Realität: Passwörter hashen
|
||||
}
|
||||
return uc.userRepository.Create(user)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package app_error
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AppError is the standard error type used throughout the application.
|
||||
type AppError struct {
|
||||
Code ErrorCode `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Status int `json:"status"` // HTTP status code
|
||||
Err error `json:"-"` // Original error, if any
|
||||
}
|
||||
|
||||
// Error satisfies the error interface.
|
||||
func (e *AppError) Error() string {
|
||||
if e.Err != nil {
|
||||
return fmt.Sprintf("Code: %s, Message: %s, Original Error: %s", e.Code, e.Message, e.Err.Error())
|
||||
}
|
||||
return fmt.Sprintf("Code: %s, Message: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
// Wrap wraps an existing error into an AppError.
|
||||
func Wrap(err error, code ErrorCode, message string, status int) *AppError {
|
||||
return &AppError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Status: status,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new AppError.
|
||||
func New(code ErrorCode, message string, status int) *AppError {
|
||||
return &AppError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Status: status,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package app_error
|
||||
|
||||
// ErrorCode is a custom type for application error codes.
|
||||
type ErrorCode string
|
||||
|
||||
const (
|
||||
// General errors
|
||||
InternalError ErrorCode = "internal_error"
|
||||
ValidationError ErrorCode = "validation_error"
|
||||
NotFoundError ErrorCode = "not_found"
|
||||
UnauthorizedError ErrorCode = "unauthorized"
|
||||
ForbiddenError ErrorCode = "forbidden"
|
||||
ConflictError ErrorCode = "conflict"
|
||||
DatabaseError ErrorCode = "database_error"
|
||||
ExternalServiceError ErrorCode = "external_service_error"
|
||||
|
||||
// Specific errors
|
||||
UserNotFoundError ErrorCode = "user_not_found"
|
||||
ProjectNotFoundError ErrorCode = "project_not_found"
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
package app_error
|
||||
|
||||
import "net/http"
|
||||
|
||||
// Helpers for creating common errors
|
||||
|
||||
// NewValidationError creates a validation error.
|
||||
func NewValidationError(message string) *AppError {
|
||||
return New(ValidationError, message, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// NewNotFoundError creates a not found error.
|
||||
func NewNotFoundError(message string) *AppError {
|
||||
return New(NotFoundError, message, http.StatusNotFound)
|
||||
}
|
||||
|
||||
// NewUnauthorizedError creates an unauthorized error.
|
||||
func NewUnauthorizedError(message string) *AppError {
|
||||
return New(UnauthorizedError, message, http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
// NewInternalError creates an internal server error.
|
||||
func NewInternalError(err error) *AppError {
|
||||
return Wrap(err, InternalError, "An internal server error occurred", http.StatusInternalServerError)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"context"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
)
|
||||
|
||||
// ProjectDataSource defines the operations for interacting with project data.
|
||||
type ProjectDataSource interface {
|
||||
Create(ctx context.Context, project entities.ProjectCreate) E.Either[error,entities.Project]
|
||||
FindByID(ctx context.Context, id string) E.Either[error,entities.Project]
|
||||
FindByUserID(ctx context.Context, userID string) E.Either[error,[]entities.Project]
|
||||
Update(ctx context.Context, project entities.ProjectUpdate) E.Either[error,entities.Project]
|
||||
Delete(ctx context.Context, id string) E.Either[error,entities.Project]
|
||||
FindAll(ctx context.Context) E.Either[error,[]entities.Project]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"context"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
)
|
||||
|
||||
// ProjectTaskDataSource defines the operations for interacting with project task data.
|
||||
type ProjectTaskDataSource interface {
|
||||
Create(ctx context.Context, task entities.ProjectTaskCreate) E.Either[error,entities.ProjectTask]
|
||||
FindByID(ctx context.Context, id string) E.Either[error,entities.ProjectTask]
|
||||
FindByProjectID(ctx context.Context, projectID string) E.Either[error,[]entities.ProjectTask]
|
||||
Update(ctx context.Context, task entities.ProjectTaskUpdate) E.Either[error,entities.ProjectTask]
|
||||
Delete(ctx context.Context, id string) E.Either[error,entities.ProjectTask]
|
||||
FindAll(ctx context.Context) E.Either[error,[]entities.ProjectTask]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"context"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
)
|
||||
|
||||
// TimeEntryDataSource defines the operations for interacting with time entry data.
|
||||
type TimeEntryDataSource interface {
|
||||
Create(ctx context.Context, entry entities.TimeEntryCreate) E.Either[error,entities.TimeEntry]
|
||||
FindByID(ctx context.Context, id string) E.Either[error,entities.TimeEntry]
|
||||
FindByUserID(ctx context.Context, userID string) E.Either[error,[]entities.TimeEntry]
|
||||
FindByProjectID(ctx context.Context, projectID string) E.Either[error,[]entities.TimeEntry]
|
||||
Update(ctx context.Context, entry entities.TimeEntryUpdate) E.Either[error,entities.TimeEntry]
|
||||
Delete(ctx context.Context, id string) E.Either[error,entities.TimeEntry]
|
||||
FindAll(ctx context.Context) E.Either[error,[]entities.TimeEntry]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"context"
|
||||
E "github.com/IBM/fp-go/either"
|
||||
)
|
||||
|
||||
// UserDataSource defines the operations for interacting with user data.
|
||||
type UserDataSource interface {
|
||||
Create(ctx context.Context, user entities.UserCreate) E.Either[error,entities.User]
|
||||
FindByID(ctx context.Context, id string) E.Either[error,entities.User]
|
||||
FindByEmail(ctx context.Context, email string) E.Either[error,entities.User]
|
||||
Update(ctx context.Context, user entities.UserUpdate) E.Either[error,entities.User]
|
||||
Delete(ctx context.Context, id string) E.Either[error,entities.User]
|
||||
FindAll(ctx context.Context) E.Either[error,[]entities.User]
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package entities
|
||||
|
||||
import "time"
|
||||
|
||||
// Project Domain
|
||||
type Project struct {
|
||||
ID string
|
||||
Name string
|
||||
Description *string
|
||||
ClientID *string
|
||||
UserID string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// ProjectCreate
|
||||
type ProjectCreate struct {
|
||||
Name string
|
||||
Description *string
|
||||
ClientID *string
|
||||
UserID string
|
||||
}
|
||||
|
||||
// ProjectUpdate
|
||||
type ProjectUpdate struct {
|
||||
ID string
|
||||
Name *string
|
||||
Description *string
|
||||
ClientID *string
|
||||
UserID *string
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package entities
|
||||
|
||||
import "time"
|
||||
|
||||
// ProjectTask Domain
|
||||
type ProjectTask struct {
|
||||
ID string
|
||||
Name string
|
||||
Description *string
|
||||
ProjectID string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// ProjectTaskCreate
|
||||
type ProjectTaskCreate struct {
|
||||
Name string
|
||||
Description *string
|
||||
ProjectID string
|
||||
}
|
||||
|
||||
// ProjectTaskUpdate
|
||||
type ProjectTaskUpdate struct {
|
||||
ID string
|
||||
Name *string
|
||||
Description *string
|
||||
ProjectID *string
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package entities
|
||||
|
||||
import "time"
|
||||
|
||||
// TimeEntry Domain
|
||||
type TimeEntry struct {
|
||||
ID string
|
||||
StartTime time.Time
|
||||
EndTime *time.Time
|
||||
Description *string
|
||||
UserID string
|
||||
ProjectID string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// TimeEntryCreate
|
||||
type TimeEntryCreate struct {
|
||||
StartTime time.Time
|
||||
EndTime *time.Time
|
||||
Description *string
|
||||
UserID string
|
||||
ProjectID string
|
||||
}
|
||||
|
||||
// TimeEntryUpdate
|
||||
type TimeEntryUpdate struct {
|
||||
ID string
|
||||
StartTime *time.Time
|
||||
EndTime *time.Time
|
||||
Description *string
|
||||
UserID *string
|
||||
ProjectID *string
|
||||
}
|
||||
@@ -1,8 +1,30 @@
|
||||
package entities
|
||||
|
||||
import "time"
|
||||
|
||||
// In user.go
|
||||
|
||||
// User Domain
|
||||
type User struct {
|
||||
ID string
|
||||
ID string
|
||||
Name string
|
||||
Email string
|
||||
Password string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// UserCreate DTO
|
||||
type UserCreate struct {
|
||||
Name string
|
||||
Email string
|
||||
Password string
|
||||
}
|
||||
|
||||
// UserUpdate DTO
|
||||
type UserUpdate struct {
|
||||
ID string
|
||||
Name *string
|
||||
Email *string
|
||||
Password *string
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package repositories
|
||||
|
||||
import "actatempus_backend/internal/domain/entities"
|
||||
|
||||
type UserRepository interface {
|
||||
Create(user *entities.User) error
|
||||
FindByEmail(email string) (*entities.User, error)
|
||||
FindByID(id string) (*entities.User, error)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"context"
|
||||
)
|
||||
|
||||
// ProjectRepository defines the operations for interacting with project data.
|
||||
type ProjectRepository interface {
|
||||
Create(ctx context.Context, project entities.Project) (entities.Project, error)
|
||||
FindByID(ctx context.Context, id string) (entities.Project, error)
|
||||
FindByUserID(ctx context.Context, userID string) ([]entities.Project, error)
|
||||
Update(ctx context.Context, project entities.Project) (entities.Project, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
FindAll(ctx context.Context) ([]entities.Project, error)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"context"
|
||||
)
|
||||
|
||||
// ProjectTaskRepository defines the operations for interacting with project task data.
|
||||
type ProjectTaskRepository interface {
|
||||
Create(ctx context.Context, task entities.ProjectTask) (entities.ProjectTask, error)
|
||||
FindByID(ctx context.Context, id string) (entities.ProjectTask, error)
|
||||
FindByProjectID(ctx context.Context, projectID string) ([]entities.ProjectTask, error)
|
||||
Update(ctx context.Context, task entities.ProjectTask) (entities.ProjectTask, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
FindAll(ctx context.Context) ([]entities.ProjectTask, error)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"context"
|
||||
)
|
||||
|
||||
// TimeEntryRepository defines the operations for interacting with time entry data.
|
||||
type TimeEntryRepository interface {
|
||||
Create(ctx context.Context, entry entities.TimeEntry) (entities.TimeEntry, error)
|
||||
FindByID(ctx context.Context, id string) (entities.TimeEntry, error)
|
||||
FindByUserID(ctx context.Context, userID string) ([]entities.TimeEntry, error)
|
||||
FindByProjectID(ctx context.Context, projectID string) ([]entities.TimeEntry, error)
|
||||
Update(ctx context.Context, entry entities.TimeEntry) (entities.TimeEntry, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
FindAll(ctx context.Context) ([]entities.TimeEntry, error)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"context"
|
||||
)
|
||||
|
||||
// UserRepository defines the operations for interacting with user data.
|
||||
type UserRepository interface {
|
||||
Create(ctx context.Context, user entities.UserCreate) (entities.User, error)
|
||||
FindByID(ctx context.Context, id string) (entities.User, error)
|
||||
FindByEmail(ctx context.Context, email string) (entities.User, error)
|
||||
Update(ctx context.Context, user entities.UserUpdate) (entities.User, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
FindAll(ctx context.Context) ([]entities.User, error)
|
||||
}
|
||||
+25
-25
@@ -1,25 +1,25 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DatabaseURL string
|
||||
Port string
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
viper.AddConfigPath(path)
|
||||
viper.SetConfigName("config")
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Config{
|
||||
DatabaseURL: viper.GetString("database.url"),
|
||||
Port: viper.GetString("server.port"),
|
||||
}, nil
|
||||
}
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DatabaseURL string
|
||||
Port string
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
viper.AddConfigPath(path)
|
||||
viper.SetConfigName("config")
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Config{
|
||||
DatabaseURL: viper.GetString("database.url"),
|
||||
Port: viper.GetString("server.port"),
|
||||
}, nil
|
||||
}
|
||||
Executable → Regular
@@ -0,0 +1,22 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/app_error"
|
||||
"actatempus_backend/internal/infrastructure/data/db"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func handleDBError(err error, notFoundMessage string) 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
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"actatempus_backend/internal/infrastructure/data/db"
|
||||
)
|
||||
|
||||
// Maps a Prisma User model to the domain User model.
|
||||
func mapPrismaUserToDomain(user db.UserDboModel) entities.User {
|
||||
return entities.User{
|
||||
ID: user.ID,
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
Password: user.Password,
|
||||
CreatedAt: user.CreatedAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// Maps a slice of Prisma User models to domain User models.
|
||||
func mapPrismaUsersToDomain(users []db.UserDboModel) []entities.User {
|
||||
domainUsers := make([]entities.User, len(users))
|
||||
for i, user := range users {
|
||||
domainUsers[i] = mapPrismaUserToDomain(user)
|
||||
}
|
||||
return domainUsers
|
||||
}
|
||||
|
||||
func mapPrismaProjectToDomain(project db.ProjectDboModel) entities.Project {
|
||||
return entities.Project{
|
||||
ID: project.ID,
|
||||
Name: project.Name,
|
||||
UserID: project.UserID,
|
||||
Description: NullableField(project.Description),
|
||||
ClientID: NullableField(project.ClientID),
|
||||
CreatedAt: project.CreatedAt,
|
||||
UpdatedAt: project.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func mapPrismaProjectsToDomain(projects []db.ProjectDboModel) []entities.Project {
|
||||
domainProjects := make([]entities.Project, len(projects))
|
||||
for i, project := range projects {
|
||||
domainProjects[i] = mapPrismaProjectToDomain(project)
|
||||
}
|
||||
return domainProjects
|
||||
}
|
||||
|
||||
func mapPrismaTimeEntryToDomain(timeEntry db.TimeEntryDboModel) entities.TimeEntry {
|
||||
return entities.TimeEntry{
|
||||
ID: timeEntry.ID,
|
||||
ProjectID: timeEntry.ProjectID,
|
||||
UserID: timeEntry.UserID,
|
||||
StartTime: timeEntry.StartTime,
|
||||
EndTime: NullableField(timeEntry.EndTime),
|
||||
Description: NullableField(timeEntry.Description),
|
||||
CreatedAt: timeEntry.CreatedAt,
|
||||
UpdatedAt: timeEntry.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func mapPrismaTimeEntriesToDomain(timeEntries []db.TimeEntryDboModel) []entities.TimeEntry {
|
||||
domainTimeEntries := make([]entities.TimeEntry, len(timeEntries))
|
||||
for i, timeEntry := range timeEntries {
|
||||
domainTimeEntries[i] = mapPrismaTimeEntryToDomain(timeEntry)
|
||||
}
|
||||
return domainTimeEntries
|
||||
}
|
||||
|
||||
func mapPrismaProjectTaskToDomain(projectTask db.ProjectTaskDboModel) entities.ProjectTask {
|
||||
return entities.ProjectTask{
|
||||
ID: projectTask.ID,
|
||||
ProjectID: projectTask.ProjectID,
|
||||
Name: projectTask.Name,
|
||||
Description: NullableField(projectTask.Description),
|
||||
CreatedAt: projectTask.CreatedAt,
|
||||
UpdatedAt: projectTask.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func mapPrismaProjectTasksToDomain(projectTasks []db.ProjectTaskDboModel) []entities.ProjectTask {
|
||||
domainProjectTasks := make([]entities.ProjectTask, len(projectTasks))
|
||||
for i, projectTask := range projectTasks {
|
||||
domainProjectTasks[i] = mapPrismaProjectTaskToDomain(projectTask)
|
||||
}
|
||||
return domainProjectTasks
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/data"
|
||||
"actatempus_backend/internal/infrastructure/data/db"
|
||||
"context"
|
||||
"log"
|
||||
)
|
||||
|
||||
// PrismaDatabase provides access to various data sources and manages the Prisma client.
|
||||
type PrismaDatabase struct {
|
||||
client *db.PrismaClient
|
||||
users data.UserDataSource
|
||||
timeEntries data.TimeEntryDataSource
|
||||
projectTasks data.ProjectTaskDataSource
|
||||
projects data.ProjectDataSource
|
||||
}
|
||||
|
||||
// NewPrismaDatabase initializes the Prisma client and the data sources.
|
||||
func NewPrismaDatabase() (*PrismaDatabase, error) {
|
||||
// Create a new Prisma client
|
||||
client := db.NewClient()
|
||||
|
||||
// Test the connection to ensure the client is working
|
||||
if err := client.Connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize the database and data sources
|
||||
db := &PrismaDatabase{
|
||||
client: client,
|
||||
users: NewPrismaUserDataSource(client),
|
||||
timeEntries: NewPrismaTimeEntryDataSource(client),
|
||||
projectTasks: NewPrismaProjectTaskDataSource(client),
|
||||
projects: NewPrismaProjectDataSource(client),
|
||||
}
|
||||
|
||||
log.Println("Database initialized")
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// Users returns the User data source.
|
||||
func (db *PrismaDatabase) Users() data.UserDataSource {
|
||||
return db.users
|
||||
}
|
||||
|
||||
// TimeEntries returns the TimeEntry data source.
|
||||
func (db *PrismaDatabase) TimeEntries() data.TimeEntryDataSource {
|
||||
return db.timeEntries
|
||||
}
|
||||
|
||||
// ProjectTasks returns the ProjectTask data source.
|
||||
func (db *PrismaDatabase) ProjectTasks() data.ProjectTaskDataSource {
|
||||
return db.projectTasks
|
||||
}
|
||||
|
||||
// Projects returns the Project data source.
|
||||
func (db *PrismaDatabase) Projects() data.ProjectDataSource {
|
||||
return db.projects
|
||||
}
|
||||
|
||||
// Close releases the Prisma client connection.
|
||||
func (db *PrismaDatabase) Close(ctx context.Context) error {
|
||||
log.Println("Closing database connection")
|
||||
return db.client.Disconnect();
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/app_error"
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"actatempus_backend/internal/infrastructure/data/db"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
)
|
||||
|
||||
type PrismaProjectDataSource struct {
|
||||
client *db.PrismaClient
|
||||
}
|
||||
|
||||
func NewPrismaProjectDataSource(client *db.PrismaClient) *PrismaProjectDataSource {
|
||||
return &PrismaProjectDataSource{client: client}
|
||||
}
|
||||
|
||||
// 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),
|
||||
),
|
||||
db.ProjectDbo.UserID.Set(project.UserID),
|
||||
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))
|
||||
}
|
||||
|
||||
if createdProject == nil {
|
||||
return E.Left[entities.Project,error](app_error.NewInternalError(fmt.Errorf("Could not create project")))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaProjectToDomain(*createdProject))
|
||||
}
|
||||
|
||||
// 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)))
|
||||
}
|
||||
|
||||
if project == nil {
|
||||
return E.Left[entities.Project,error](app_error.NewNotFoundError(fmt.Sprintf("Project with ID %s not found", id)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaProjectToDomain(*project))
|
||||
}
|
||||
|
||||
// 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),
|
||||
),
|
||||
).Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return E.Left[entities.Project](handleDBError(err, fmt.Sprintf("Could not update project with ID %s", project.ID)))
|
||||
}
|
||||
|
||||
if updatedProject == nil {
|
||||
return E.Left[entities.Project,error](app_error.NewNotFoundError(fmt.Sprintf("Project with ID %s not found", project.ID)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaProjectToDomain(*updatedProject))
|
||||
}
|
||||
|
||||
// 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)))
|
||||
}
|
||||
|
||||
if deleted == nil {
|
||||
return E.Left[entities.Project,error](app_error.NewNotFoundError(fmt.Sprintf("Project with ID %s not found", id)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaProjectToDomain(*deleted))
|
||||
}
|
||||
|
||||
// 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 E.Right[error](mapPrismaProjectsToDomain(projects))
|
||||
}
|
||||
|
||||
// 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 E.Right[error](mapPrismaProjectsToDomain(projects))
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/app_error"
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"actatempus_backend/internal/infrastructure/data/db"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
)
|
||||
|
||||
type PrismaProjectTaskDataSource struct {
|
||||
client *db.PrismaClient
|
||||
}
|
||||
|
||||
func NewPrismaProjectTaskDataSource(client *db.PrismaClient) *PrismaProjectTaskDataSource {
|
||||
return &PrismaProjectTaskDataSource{client: client}
|
||||
}
|
||||
|
||||
// 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),
|
||||
),
|
||||
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")))
|
||||
}
|
||||
|
||||
if createdTask == nil {
|
||||
return E.Left[entities.ProjectTask,error](app_error.NewInternalError(fmt.Errorf("Could not create project task")))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaProjectTaskToDomain(*createdTask))
|
||||
}
|
||||
|
||||
// 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)))
|
||||
}
|
||||
|
||||
if task == nil {
|
||||
return E.Left[entities.ProjectTask,error](app_error.NewNotFoundError(fmt.Sprintf("ProjectTask with ID %s not found", id)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaProjectTaskToDomain(*task))
|
||||
}
|
||||
|
||||
// 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),
|
||||
),
|
||||
).Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return E.Left[entities.ProjectTask](handleDBError(err, fmt.Sprintf("Could not update project task with ID %s", task.ID)))
|
||||
}
|
||||
|
||||
if updatedTask == nil {
|
||||
return E.Left[entities.ProjectTask,error](app_error.NewNotFoundError(fmt.Sprintf("ProjectTask with ID %s not found", task.ID)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaProjectTaskToDomain(*updatedTask))
|
||||
}
|
||||
|
||||
// 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)))
|
||||
}
|
||||
|
||||
if deletedTask == nil {
|
||||
return E.Left[entities.ProjectTask,error](app_error.NewNotFoundError(fmt.Sprintf("ProjectTask with ID %s not found", id)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaProjectTaskToDomain(*deletedTask))
|
||||
}
|
||||
|
||||
// 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 E.Right[error](mapPrismaProjectTasksToDomain(tasks))
|
||||
}
|
||||
|
||||
// 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 E.Right[error](mapPrismaProjectTasksToDomain(tasks))
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/app_error"
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"actatempus_backend/internal/infrastructure/data/db"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
)
|
||||
|
||||
type PrismaTimeEntryDataSource struct {
|
||||
client *db.PrismaClient
|
||||
}
|
||||
|
||||
func NewPrismaTimeEntryDataSource(client *db.PrismaClient) *PrismaTimeEntryDataSource {
|
||||
return &PrismaTimeEntryDataSource{client: client}
|
||||
}
|
||||
|
||||
// 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),
|
||||
),
|
||||
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))
|
||||
}
|
||||
|
||||
if createdEntry == nil {
|
||||
return E.Left[entities.TimeEntry,error](app_error.NewInternalError(fmt.Errorf("Could not create time entry")))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaTimeEntryToDomain(*createdEntry))
|
||||
}
|
||||
|
||||
// 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)))
|
||||
}
|
||||
|
||||
if entry == nil {
|
||||
return E.Left[entities.TimeEntry,error](app_error.NewNotFoundError(fmt.Sprintf("TimeEntry with ID %s not found", id)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaTimeEntryToDomain(*entry))
|
||||
}
|
||||
|
||||
// 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),
|
||||
),
|
||||
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)))
|
||||
}
|
||||
|
||||
if updatedEntry == nil {
|
||||
return E.Left[entities.TimeEntry,error](app_error.NewNotFoundError(fmt.Sprintf("TimeEntry with ID %s not found", entry.ID)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaTimeEntryToDomain(*updatedEntry))
|
||||
}
|
||||
|
||||
// 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)))
|
||||
}
|
||||
|
||||
if deletedEntry == nil {
|
||||
return E.Left[entities.TimeEntry,error](app_error.NewNotFoundError(fmt.Sprintf("TimeEntry with ID %s not found", id)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaTimeEntryToDomain(*deletedEntry))
|
||||
}
|
||||
|
||||
// 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 E.Right[error](mapPrismaTimeEntriesToDomain(entries))
|
||||
}
|
||||
|
||||
// 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 E.Right[error](mapPrismaTimeEntriesToDomain(entries))
|
||||
}
|
||||
|
||||
|
||||
// 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 E.Right[error](mapPrismaTimeEntriesToDomain(entries))
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/domain/app_error"
|
||||
"actatempus_backend/internal/domain/entities"
|
||||
"actatempus_backend/internal/infrastructure/data/db"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
E "github.com/IBM/fp-go/either"
|
||||
)
|
||||
|
||||
type PrismaUserDataSource struct {
|
||||
client *db.PrismaClient
|
||||
}
|
||||
|
||||
func NewPrismaUserDataSource(client *db.PrismaClient) *PrismaUserDataSource {
|
||||
return &PrismaUserDataSource{client: client}
|
||||
}
|
||||
|
||||
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(user.Password),
|
||||
).Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return E.Left[entities.User,error](app_error.NewInternalError(err))
|
||||
}
|
||||
if createdUser == nil {
|
||||
return E.Left[entities.User,error](app_error.NewInternalError(fmt.Errorf("Could not create user")))
|
||||
}
|
||||
return E.Right[error](mapPrismaUserToDomain(*createdUser))
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return E.Left[entities.User,error](app_error.NewNotFoundError(fmt.Sprintf("User with ID %s not found", id)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaUserToDomain(*user))
|
||||
}
|
||||
|
||||
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)))
|
||||
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return E.Left[entities.User,error](app_error.NewNotFoundError(fmt.Sprintf("User with email %s not found", email)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaUserToDomain(*user))
|
||||
}
|
||||
|
||||
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(user.Password),
|
||||
).Exec(ctx)
|
||||
|
||||
if err != nil {
|
||||
return E.Left[entities.User,error](handleDBError(err, fmt.Sprintf("Could not update user with ID %s", user.ID)))
|
||||
}
|
||||
|
||||
if updatedUser == nil {
|
||||
return E.Left[entities.User,error](app_error.NewNotFoundError(fmt.Sprintf("User with ID %s not found", user.ID)))
|
||||
}
|
||||
|
||||
return E.Right[error](mapPrismaUserToDomain(*updatedUser))
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
if deleted == nil {
|
||||
return E.Left[entities.User,error](app_error.NewNotFoundError(fmt.Sprintf("User with ID %s not found", id)))
|
||||
}
|
||||
|
||||
|
||||
return E.Right[error](mapPrismaUserToDomain(*deleted))
|
||||
}
|
||||
|
||||
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 E.Right[error](mapPrismaUsersToDomain(users))
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"actatempus_backend/internal/infrastructure/persistence/config"
|
||||
"actatempus_backend/internal/infrastructure/config"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package utils
|
||||
|
||||
// Let applies a function `f` to a pointer value `ptr` if it's not nil.
|
||||
// Returns nil if the pointer is nil.
|
||||
func Let[T any, R any](ptr *T, f func(T) R) *R {
|
||||
if ptr == nil {
|
||||
return nil
|
||||
}
|
||||
result := f(*ptr)
|
||||
return &result
|
||||
}
|
||||
Reference in New Issue
Block a user