feat: implement rest server with Fiber and OpenAPI spec, bump deps, use training.InMemoryTrainingRepository
This commit is contained in:
parent
263bed126e
commit
58b017a59f
5 changed files with 297 additions and 10 deletions
132
internal/server/api.go
Normal file
132
internal/server/api.go
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gitlab.mareshq.com/hq/yggdrasil/pkg/training"
|
||||
)
|
||||
|
||||
type APIHandlers struct {
|
||||
trainingRepository training.TrainingRepository
|
||||
}
|
||||
|
||||
func NewAPIHandlers(trainingRepository training.TrainingRepository) *APIHandlers {
|
||||
return &APIHandlers{
|
||||
trainingRepository: trainingRepository,
|
||||
}
|
||||
}
|
||||
|
||||
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, training := range trainings {
|
||||
data[idx] = Training{
|
||||
Id: training.ID,
|
||||
Name: training.Name,
|
||||
Days: training.Days,
|
||||
}
|
||||
}
|
||||
|
||||
return ListTrainings200JSONResponse(data), nil
|
||||
}
|
||||
|
||||
func (h *APIHandlers) CreateTraining(ctx context.Context, req CreateTrainingRequestObject) (CreateTrainingResponseObject, error) {
|
||||
t := training.Training{
|
||||
Name: req.Body.Name,
|
||||
Days: req.Body.Days,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return CreateTraining201JSONResponse{
|
||||
Id: t.ID,
|
||||
Name: t.Name,
|
||||
Days: t.Days,
|
||||
}, 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
|
||||
}
|
||||
|
||||
return GetTraining200JSONResponse{
|
||||
Id: t.ID,
|
||||
Name: t.Name,
|
||||
Days: t.Days,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *APIHandlers) UpdateTraining(ctx context.Context, req UpdateTrainingRequestObject) (UpdateTrainingResponseObject, error) {
|
||||
t := training.Training{
|
||||
ID: req.TrainingID,
|
||||
Name: req.Body.Name,
|
||||
Days: req.Body.Days,
|
||||
}
|
||||
|
||||
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,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -6,19 +6,23 @@ import (
|
|||
"time"
|
||||
|
||||
fiberzap "github.com/gofiber/contrib/fiberzap/v2"
|
||||
"github.com/gofiber/contrib/swagger"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
middleware "github.com/oapi-codegen/fiber-middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
port int
|
||||
logger *zap.Logger
|
||||
port int
|
||||
logger *zap.Logger
|
||||
apiHandlers *APIHandlers
|
||||
}
|
||||
|
||||
func NewServer(port int, logger *zap.Logger) *Server {
|
||||
func NewServer(apiHandlers *APIHandlers, port int, logger *zap.Logger) *Server {
|
||||
return &Server{
|
||||
port: port,
|
||||
logger: logger,
|
||||
apiHandlers: apiHandlers,
|
||||
port: port,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,9 +43,28 @@ func (s *Server) Run(ctx context.Context) {
|
|||
Logger: s.logger,
|
||||
}))
|
||||
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.SendString("Hello, World!")
|
||||
})
|
||||
swaggerConfig := swagger.Config{
|
||||
BasePath: "/",
|
||||
FilePath: "./api/v1/openapi.yaml",
|
||||
Path: "/swagger",
|
||||
Title: "Swagger API Docs",
|
||||
}
|
||||
app.Use(swagger.New(swaggerConfig))
|
||||
|
||||
swagger, err := GetSwagger()
|
||||
if err != nil {
|
||||
sugared.Fatal("Error getting swagger", zap.Error(err))
|
||||
}
|
||||
|
||||
// See: https://github.com/deepmap/oapi-codegen/blob/master/examples/petstore-expanded/fiber/petstore.go#L41
|
||||
// Clear out the servers array in the swagger spec, that skips validating
|
||||
// that server names match. We don't know how this thing will be run.
|
||||
swagger.Servers = nil
|
||||
|
||||
app.Use(middleware.OapiRequestValidator(swagger))
|
||||
|
||||
handlers := NewStrictHandler(s.apiHandlers, nil)
|
||||
RegisterHandlers(app, handlers)
|
||||
|
||||
go func() {
|
||||
err := app.Listen(fmt.Sprintf(":%d", s.port))
|
||||
|
|
@ -56,7 +79,7 @@ func (s *Server) Run(ctx context.Context) {
|
|||
shutdownBegan := time.Now()
|
||||
|
||||
sugared.Infoln("Shutting HTTP server down gracefully...")
|
||||
err := app.ShutdownWithTimeout(10 * time.Second)
|
||||
err = app.ShutdownWithTimeout(10 * time.Second)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue