package server import ( "context" "time" "github.com/gofiber/fiber/v2" "gitlab.mareshq.com/hq/yggdrasil/internal/money" "gitlab.mareshq.com/hq/yggdrasil/pkg/training" ) type APIHandlers struct { trainingRepository training.TrainingRepository trainingDateRepository training.TrainingDateRepository } func NewAPIHandlers(trainingRepository training.TrainingRepository, trainingDateRepository training.TrainingDateRepository) *APIHandlers { return &APIHandlers{ trainingRepository: trainingRepository, trainingDateRepository: trainingDateRepository, } } func (h *APIHandlers) ListTrainings(ctx context.Context, req ListTrainingsRequestObject) (ListTrainingsResponseObject, error) { trainings, err := h.trainingRepository.FindAll() if err != nil { return ListTrainings500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to list trainings", Detail: err.Error(), }}, nil } data := make([]Training, len(trainings)) for idx, t := range trainings { pricing := make([]TrainingPrice, len(t.Pricing)) for idx, p := range t.Pricing { pricing[idx] = TrainingPrice{ Amount: p.Amount, Currency: p.Currency, Type: TrainingPriceType(p.Type), } } data[idx] = Training{ Id: t.ID, Name: t.Name, Days: t.Days, Description: t.Description, Pricing: pricing, } } return ListTrainings200JSONResponse(data), nil } func (h *APIHandlers) CreateTraining(ctx context.Context, req CreateTrainingRequestObject) (CreateTrainingResponseObject, error) { pricing := make([]training.TrainingPrice, len(req.Body.Pricing)) for idx, p := range req.Body.Pricing { pricing[idx] = training.TrainingPrice{ Amount: p.Amount, Currency: p.Currency, Type: training.TrainingPriceType(p.Type), } } t := training.Training{ Name: req.Body.Name, Days: req.Body.Days, Description: req.Body.Description, Pricing: pricing, } err := h.trainingRepository.Create(&t) if err != nil { return CreateTraining500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to create training", Detail: err.Error(), }}, nil } responsePricing := make([]TrainingPrice, len(t.Pricing)) for idx, p := range t.Pricing { responsePricing[idx] = TrainingPrice{ Amount: p.Amount, Currency: p.Currency, Type: TrainingPriceType(p.Type), } } return CreateTraining201JSONResponse{ Id: t.ID, Name: t.Name, Days: t.Days, Description: t.Description, Pricing: responsePricing, }, nil } func (h *APIHandlers) DeleteTraining(ctx context.Context, req DeleteTrainingRequestObject) (DeleteTrainingResponseObject, error) { err := h.trainingRepository.Delete(req.TrainingID) if err == training.ErrTrainingNotFound { return DeleteTraining404ApplicationProblemPlusJSONResponse{ NotFoundErrorApplicationProblemPlusJSONResponse: NotFoundErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusNotFound, Title: "Not Found: Training not found", }}, nil } else if err != nil { return DeleteTraining500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to delete training", Detail: err.Error(), }}, nil } return DeleteTraining204Response{}, nil } func (h *APIHandlers) GetTraining(ctx context.Context, req GetTrainingRequestObject) (GetTrainingResponseObject, error) { t, err := h.trainingRepository.FindByID(req.TrainingID) if err == training.ErrTrainingNotFound { return GetTraining404ApplicationProblemPlusJSONResponse{ NotFoundErrorApplicationProblemPlusJSONResponse: NotFoundErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusNotFound, Title: "Not Found: Training not found", }}, nil } else if err != nil { return GetTraining500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to get training", Detail: err.Error(), }}, nil } pricing := make([]TrainingPrice, len(t.Pricing)) for idx, p := range t.Pricing { pricing[idx] = TrainingPrice{ Amount: p.Amount, Currency: p.Currency, Type: TrainingPriceType(p.Type), } } return GetTraining200JSONResponse{ Id: t.ID, Name: t.Name, Days: t.Days, Description: t.Description, Pricing: pricing, }, nil } func (h *APIHandlers) UpdateTraining(ctx context.Context, req UpdateTrainingRequestObject) (UpdateTrainingResponseObject, error) { pricing := make([]training.TrainingPrice, len(req.Body.Pricing)) for idx, p := range req.Body.Pricing { pricing[idx] = training.TrainingPrice{ Amount: p.Amount, Currency: p.Currency, Type: training.TrainingPriceType(p.Type), } } t := training.Training{ ID: req.TrainingID, Name: req.Body.Name, Days: req.Body.Days, Description: req.Body.Description, Pricing: pricing, } err := h.trainingRepository.Update(&t) if err != nil { return UpdateTraining500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to update training", Detail: err.Error(), }}, nil } return UpdateTraining200JSONResponse{ Id: t.ID, Name: t.Name, Days: t.Days, Description: t.Description, }, nil } const trainingDateStartTimeFormat = "15:04" func (h *APIHandlers) ListTrainingDates(ctx context.Context, req ListTrainingDatesRequestObject) (ListTrainingDatesResponseObject, error) { trainingDates, err := h.trainingDateRepository.FindAllByTrainingID(req.TrainingID) if err != nil { return ListTrainingDates500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to list training dates", Detail: err.Error(), }}, nil } data := make([]TrainingDate, len(trainingDates)) for idx, td := range trainingDates { data[idx] = TrainingDate{ Id: td.ID, Date: td.Date, StartTime: td.StartTime.Format(trainingDateStartTimeFormat), Days: td.Days, IsOnline: td.IsOnline, Location: td.Location, Address: td.Address, Capacity: td.Capacity, Price: Price{ Amount: td.Price.Amount, Currency: td.Price.Currency, }, } } return ListTrainingDates200JSONResponse(data), nil } func (h *APIHandlers) CreateTrainingDate(ctx context.Context, req CreateTrainingDateRequestObject) (CreateTrainingDateResponseObject, error) { price := money.Price{ Amount: req.Body.Price.Amount, Currency: req.Body.Price.Currency, } startTime, err := time.Parse(time.RFC3339, "2000-01-01T"+req.Body.StartTime) if err != nil { return CreateTrainingDate400ApplicationProblemPlusJSONResponse{ InvalidInputErrorApplicationProblemPlusJSONResponse: InvalidInputErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusBadRequest, Title: "Invalid Input: Invalid startTime", Detail: err.Error(), }}, nil } td := training.TrainingDate{ Date: req.Body.Date, StartTime: startTime, Days: req.Body.Days, IsOnline: req.Body.IsOnline, Location: req.Body.Location, Address: req.Body.Address, Capacity: req.Body.Capacity, Price: price, } err = h.trainingDateRepository.Create(req.TrainingID, &td) if err != nil { return CreateTrainingDate500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to create training date", Detail: err.Error(), }}, nil } return CreateTrainingDate201JSONResponse{ Id: td.ID, Date: td.Date, StartTime: td.StartTime.Format(trainingDateStartTimeFormat), Days: td.Days, IsOnline: td.IsOnline, Location: td.Location, Address: td.Address, Capacity: td.Capacity, Price: Price{ Amount: td.Price.Amount, Currency: td.Price.Currency, }, }, nil } func (h *APIHandlers) DeleteTrainingDate(ctx context.Context, req DeleteTrainingDateRequestObject) (DeleteTrainingDateResponseObject, error) { err := h.trainingDateRepository.Delete(req.TrainingDateID) if err == training.ErrTrainingDateNotFound { return DeleteTrainingDate404ApplicationProblemPlusJSONResponse{ NotFoundErrorApplicationProblemPlusJSONResponse: NotFoundErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusNotFound, Title: "Not Found: Training date not found", }}, nil } else if err != nil { return DeleteTrainingDate500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to delete training date", Detail: err.Error(), }}, nil } return DeleteTrainingDate204Response{}, nil } func (h *APIHandlers) GetTrainingDate(ctx context.Context, req GetTrainingDateRequestObject) (GetTrainingDateResponseObject, error) { td, err := h.trainingDateRepository.FindByID(req.TrainingDateID) if err == training.ErrTrainingDateNotFound { return GetTrainingDate404ApplicationProblemPlusJSONResponse{ NotFoundErrorApplicationProblemPlusJSONResponse: NotFoundErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusNotFound, Title: "Not Found: Training date not found", }}, nil } else if err != nil { return GetTrainingDate500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to get training date", Detail: err.Error(), }}, nil } return GetTrainingDate200JSONResponse{ Id: td.ID, Date: td.Date, StartTime: td.StartTime.Format(trainingDateStartTimeFormat), Days: td.Days, IsOnline: td.IsOnline, Location: td.Location, Address: td.Address, Capacity: td.Capacity, Price: Price{ Amount: td.Price.Amount, Currency: td.Price.Currency, }, }, nil } func (h *APIHandlers) UpdateTrainingDate(ctx context.Context, req UpdateTrainingDateRequestObject) (UpdateTrainingDateResponseObject, error) { price := money.Price{ Amount: req.Body.Price.Amount, Currency: req.Body.Price.Currency, } startTime, err := time.Parse(time.RFC3339, "2000-01-01T"+req.Body.StartTime) if err != nil { return UpdateTrainingDate400ApplicationProblemPlusJSONResponse{ InvalidInputErrorApplicationProblemPlusJSONResponse: InvalidInputErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusBadRequest, Title: "Invalid Input: Invalid startTime", Detail: err.Error(), }}, nil } td := training.TrainingDate{ ID: req.TrainingDateID, Date: req.Body.Date, StartTime: startTime, Days: req.Body.Days, IsOnline: req.Body.IsOnline, Location: req.Body.Location, Address: req.Body.Address, Capacity: req.Body.Capacity, Price: price, } err = h.trainingDateRepository.Update(&td) if err != nil { return UpdateTrainingDate500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to update training date", Detail: err.Error(), }}, nil } return UpdateTrainingDate200JSONResponse{ Id: td.ID, Date: td.Date, StartTime: td.StartTime.Format(trainingDateStartTimeFormat), Days: td.Days, IsOnline: td.IsOnline, Location: td.Location, Address: td.Address, Capacity: td.Capacity, Price: Price{ Amount: td.Price.Amount, Currency: td.Price.Currency, }, }, nil } func (h *APIHandlers) ListAllUpcomingTrainingDates(ctx context.Context, req ListAllUpcomingTrainingDatesRequestObject) (ListAllUpcomingTrainingDatesResponseObject, error) { trainings, err := h.trainingRepository.FindAll() if err != nil { return ListAllUpcomingTrainingDates500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to list all upcoming training dates", Detail: err.Error(), }}, nil } response := make(ListAllUpcomingTrainingDatesResponse, len(trainings)) for i, t := range trainings { trainingDates, err := h.trainingDateRepository.FindUpcomingByTrainingID(t.ID) if err != nil { return ListAllUpcomingTrainingDates500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to list all upcoming training dates", Detail: err.Error(), }}, nil } response[i].Dates = make([]TrainingDate, len(trainingDates)) response[i].TrainingID = t.ID for j, td := range trainingDates { response[i].Dates[j] = TrainingDate{ Id: td.ID, Date: td.Date, StartTime: td.StartTime.Format(trainingDateStartTimeFormat), Days: td.Days, IsOnline: td.IsOnline, Location: td.Location, Address: td.Address, Capacity: td.Capacity, Price: Price{ Amount: td.Price.Amount, Currency: td.Price.Currency, }, } } } return ListAllUpcomingTrainingDates200JSONResponse(response), nil } func (h *APIHandlers) ListTrainingUpcomingDates(ctx context.Context, req ListTrainingUpcomingDatesRequestObject) (ListTrainingUpcomingDatesResponseObject, error) { trainingDates, err := h.trainingDateRepository.FindUpcomingByTrainingID(req.TrainingID) if err != nil { return ListTrainingUpcomingDates500ApplicationProblemPlusJSONResponse{ InternalErrorApplicationProblemPlusJSONResponse: InternalErrorApplicationProblemPlusJSONResponse{ Status: fiber.StatusInternalServerError, Title: "Internal Server Error: Failed to list all upcoming training dates", Detail: err.Error(), }}, nil } data := make([]TrainingDate, len(trainingDates)) for idx, td := range trainingDates { data[idx] = TrainingDate{ Id: td.ID, Date: td.Date, StartTime: td.StartTime.Format(trainingDateStartTimeFormat), Days: td.Days, IsOnline: td.IsOnline, Location: td.Location, Address: td.Address, Capacity: td.Capacity, Price: Price{ Amount: td.Price.Amount, Currency: td.Price.Currency, }, } } return ListTrainingUpcomingDates200JSONResponse(data), nil }