WIP: EOL
This commit is contained in:
parent
7ed1e05284
commit
49e05cac10
23 changed files with 613 additions and 253 deletions
|
|
@ -29,10 +29,9 @@ const (
|
|||
|
||||
// NewTraining defines model for NewTraining.
|
||||
type NewTraining struct {
|
||||
Duration int32 `json:"duration"`
|
||||
Length *int32 `json:"length,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Price struct {
|
||||
Days int32 `json:"days"`
|
||||
Name string `json:"name"`
|
||||
Price struct {
|
||||
Corporate *Price `json:"corporate,omitempty"`
|
||||
Open *Price `json:"open,omitempty"`
|
||||
} `json:"price"`
|
||||
|
|
@ -90,11 +89,10 @@ type ProblemDetails struct {
|
|||
|
||||
// Training defines model for Training.
|
||||
type Training struct {
|
||||
Duration int32 `json:"duration"`
|
||||
Id training.ID `json:"id"`
|
||||
Length *int32 `json:"length,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Price struct {
|
||||
Days int32 `json:"days"`
|
||||
Id training.ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Price struct {
|
||||
Corporate *Price `json:"corporate,omitempty"`
|
||||
Open *Price `json:"open,omitempty"`
|
||||
} `json:"price"`
|
||||
|
|
@ -1675,40 +1673,39 @@ func (sh *strictHandler) ListTrainingFeedback(ctx *fiber.Ctx, trainingID trainin
|
|||
// Base64 encoded, gzipped, json marshaled Swagger object
|
||||
var swaggerSpec = []string{
|
||||
|
||||
"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",
|
||||
"RgAA",
|
||||
"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",
|
||||
}
|
||||
|
||||
// GetSwagger returns the content of the embedded swagger specification file
|
||||
|
|
|
|||
126
internal/api/server.go
Normal file
126
internal/api/server.go
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/internal/postgres"
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type APIServer struct {
|
||||
logger *zap.SugaredLogger
|
||||
|
||||
trainingRepo *postgres.TrainingRepository
|
||||
dateRepo *postgres.DateRepository
|
||||
attendeeRepo *postgres.AttendeeRepository
|
||||
}
|
||||
|
||||
func NewAPIServer(db *sql.DB, logger *zap.SugaredLogger) *APIServer {
|
||||
return &APIServer{
|
||||
logger: logger,
|
||||
|
||||
trainingRepo: postgres.NewTrainingRepository(db),
|
||||
dateRepo: postgres.NewDateRepository(db),
|
||||
attendeeRepo: postgres.NewAttendeeRepository(db),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *APIServer) ListTrainings(c *fiber.Ctx) error {
|
||||
all, err := s.trainingRepo.FindAll()
|
||||
if err != nil {
|
||||
s.logger.Error("ListTrainings", zap.Error(err))
|
||||
c.JSON(ListTrainings500ApplicationProblemPlusJSONResponse{
|
||||
Status: fiber.StatusInternalServerError,
|
||||
Title: fmt.Sprintf("Internal server error: %s", "failed to list trainings"),
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(ListTrainings200JSONResponse{})
|
||||
}
|
||||
|
||||
func (s *APIServer) CreateTraining(c *fiber.Ctx) error {
|
||||
tr := &training.Training{}
|
||||
if err := c.BodyParser(tr); err != nil {
|
||||
s.logger.Error("CreateTraining", zap.Error(err))
|
||||
c.JSON(CreateTraining400ApplicationProblemPlusJSONResponse{
|
||||
Status: fiber.StatusBadRequest,
|
||||
Title: fmt.Sprintf("Internal server error: %s", "failed to parse request body"),
|
||||
})
|
||||
}
|
||||
|
||||
if err := s.trainingRepo.Create(tr); err != nil {
|
||||
s.logger.Error("CreateTraining", zap.Error(err))
|
||||
c.JSON(CreateTraining500ApplicationProblemPlusJSONResponse{
|
||||
Status: fiber.StatusInternalServerError,
|
||||
Title: fmt.Sprintf("Internal server error: %s", "failed to save training"),
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(CreateTraining201JSONResponse{
|
||||
Id: tr.ID,
|
||||
Name: tr.Name,
|
||||
Days: int32(tr.Days),
|
||||
Price: tr.Price,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (s *APIServer) UpdateTraining(c *fiber.Ctx) error {
|
||||
s.logger.Info("UpdateTraining")
|
||||
}
|
||||
|
||||
func (s *APIServer) DeleteTraining(c *fiber.Ctx, trainingID training.ID) error {
|
||||
s.logger.Info("DeleteTraining")
|
||||
}
|
||||
|
||||
func (s *APIServer) GetTraining(c *fiber.Ctx, trainingID training.ID) error {
|
||||
s.logger.Info("GetTraining")
|
||||
}
|
||||
|
||||
func (s *APIServer) ListTrainingDates(c *fiber.Ctx, trainingID training.ID) error {
|
||||
s.logger.Info("ListTrainingDates")
|
||||
}
|
||||
|
||||
func (s *APIServer) CreateTrainingDate(c *fiber.Ctx, trainingID training.ID) error {
|
||||
s.logger.Info("CreateTrainingDate")
|
||||
}
|
||||
|
||||
func (s *APIServer) UpdateTrainingDate(c *fiber.Ctx, trainingID training.ID, dateID training.DateID) error {
|
||||
s.logger.Info("UpdateTrainingDate")
|
||||
}
|
||||
|
||||
func (s *APIServer) DeleteTrainingDate(c *fiber.Ctx, trainingID training.ID, dateID training.DateID) error {
|
||||
s.logger.Info("DeleteTrainingDate")
|
||||
}
|
||||
|
||||
func (s *APIServer) ListUpcomingTrainingDates(c *fiber.Ctx, params ListUpcomingTrainingDatesParams) error {
|
||||
s.logger.Info("ListUpcomingTrainingDates")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *APIServer) ListTrainingDateAttendees(c *fiber.Ctx, trainingID training.ID, dateID training.DateID) error {
|
||||
s.logger.Info("ListTrainingDateAttendees")
|
||||
}
|
||||
|
||||
func (s *APIServer) CreateTrainingDateAttendee(c *fiber.Ctx, trainingID training.ID, dateID training.DateID) error {
|
||||
s.logger.Info("CreateTrainingDateAttendee")
|
||||
}
|
||||
|
||||
func (s *APIServer) DeleteTrainingDateAttendee(c *fiber.Ctx, trainingID training.ID, dateID training.DateID, attendeeID training.AttendeeID) error {
|
||||
s.logger.Info("DeleteTrainingDateAttendee")
|
||||
}
|
||||
|
||||
func (s *APIServer) CreateTrainingDateAttendeeFeedback(c *fiber.Ctx, trainingID training.ID, dateID training.DateID, attendeeID training.AttendeeID) error {
|
||||
s.logger.Info("CreateTrainingDateAttendeeFeedback")
|
||||
}
|
||||
|
||||
func (s *APIServer) ListTrainingDateFeedback(c *fiber.Ctx, trainingID training.ID, dateID training.DateID) error {
|
||||
s.logger.Info("ListTrainingDateFeedback")
|
||||
}
|
||||
|
||||
func (s *APIServer) ListTrainingFeedback(c *fiber.Ctx, trainingID training.ID) error {
|
||||
s.logger.Info("ListTrainingFeedback")
|
||||
}
|
||||
13
internal/currency/currency.go
Normal file
13
internal/currency/currency.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package currency
|
||||
|
||||
type Currency string
|
||||
|
||||
var (
|
||||
Currencies = []Currency{CZK, EUR, USD}
|
||||
)
|
||||
|
||||
const (
|
||||
CZK Currency = "CZK"
|
||||
EUR Currency = "EUR"
|
||||
USD Currency = "USD"
|
||||
)
|
||||
|
|
@ -77,6 +77,7 @@ func (r *AttendeeRepository) CountForDate(dateID training.DateID) (int, error) {
|
|||
}
|
||||
|
||||
func (r *AttendeeRepository) Create(a *training.Attendee) error {
|
||||
a.ID = training.NewAttendeeID()
|
||||
_, err := r.db.Exec("INSERT INTO attendee (id, date_id, name, email, company, role, is_student, has_attended, has_paid) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", a.ID, a.DateID, a.Name, a.Email, a.Company, a.Role, a.IsStudent, a.HasAttended, a.HasPaid)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ func (r *DateRepository) FindAllForTraining(id training.ID) ([]training.Date, er
|
|||
}
|
||||
|
||||
func (r *DateRepository) Create(d *training.Date) error {
|
||||
d.ID = training.NewDateID()
|
||||
_, err := r.db.Exec("INSERT INTO date (id, date, training_id, start_time, days, price, is_online, location, address, capacity) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", d.ID, d.Date, d.TrainingID, d.StartTime, d.Days, d.Price, d.IsOnline, d.Location, d.Address, d.Capacity)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ func NewTrainingRepository(db *sql.DB) *TrainingRepository {
|
|||
}
|
||||
|
||||
func (r *TrainingRepository) Create(t *training.Training) error {
|
||||
t.ID = training.NewID()
|
||||
_, err := r.db.Exec("INSERT INTO training (id, name, days, description, price) VALUES ($1, $2, $3)", t.ID, t.Name, t.Days, t.Description, t.Price)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
7
internal/server/config.go
Normal file
7
internal/server/config.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package server
|
||||
|
||||
type Config struct {
|
||||
PostgresURL string
|
||||
Port int
|
||||
ShutdownGraceSeconds int
|
||||
}
|
||||
145
internal/server/server.go
Normal file
145
internal/server/server.go
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"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"
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/internal/api"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
ready ready
|
||||
}
|
||||
|
||||
func NewServer() *Server {
|
||||
return &Server{}
|
||||
}
|
||||
|
||||
func (s *Server) Run() {
|
||||
shutdownCtx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
logger.Fatal("failed to initialize logger", zap.Error(err))
|
||||
}
|
||||
defer logger.Sync()
|
||||
sugaredLogger := logger.Sugar()
|
||||
|
||||
fiberConfig := fiber.Config{
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 5 * time.Second,
|
||||
IdleTimeout: 5 * time.Second,
|
||||
|
||||
DisableStartupMessage: true,
|
||||
}
|
||||
|
||||
app := fiber.New(fiberConfig)
|
||||
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
})
|
||||
|
||||
app.Get("/livez", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
})
|
||||
|
||||
app.Get("/readyz", func(c *fiber.Ctx) error {
|
||||
if s.ready.isReady() {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusServiceUnavailable)
|
||||
})
|
||||
|
||||
// TODO: add /metrics endpoint
|
||||
|
||||
app.Use(fiberzap.New(fiberzap.Config{
|
||||
Logger: logger,
|
||||
}))
|
||||
|
||||
swaggerConfig := swagger.Config{
|
||||
BasePath: "/",
|
||||
FilePath: "./api/v1/openapi.yaml",
|
||||
Path: "/swagger",
|
||||
Title: "Swagger API Docs",
|
||||
}
|
||||
|
||||
app.Use(swagger.New(swaggerConfig))
|
||||
|
||||
app = registerAPIHandlers(app, sugaredLogger)
|
||||
|
||||
<-shutdownCtx.Done()
|
||||
stop()
|
||||
s.ready.notReady()
|
||||
|
||||
// wait till Pod is signaling not ready (no new requests)
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
shutdownBegan := time.Now()
|
||||
|
||||
sugaredLogger.Info("Shutdown signal recieved. Shutting down gracefully...")
|
||||
|
||||
// * Gracefully shut down fiber server
|
||||
// * If does not shutdown within `gracefulShutdownPeriod`, force shutdown
|
||||
// ! Does not close keep-alive connections
|
||||
// ! fiber.Config{ReadTimeout} must be set and non-zero and greater than the `gracefulShutdownPeriod`
|
||||
// TODO: make shutdown timeout configurable
|
||||
err = app.ShutdownWithTimeout(time.Second * 10)
|
||||
if err != nil {
|
||||
sugaredLogger.Errorw("Error during shutdown", "error", err)
|
||||
}
|
||||
|
||||
sugaredLogger.Infof("Shutdown completed in %v", time.Since(shutdownBegan))
|
||||
}
|
||||
|
||||
func registerAPIHandlers(fiberApp *fiber.App, logger *zap.SugaredLogger) *fiber.App {
|
||||
swagger, err := api.GetSwagger()
|
||||
if err != nil {
|
||||
logger.Fatalf("Error getting swagger: %v", err)
|
||||
}
|
||||
|
||||
// TODO: validate this. Copied from example
|
||||
// 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
|
||||
|
||||
fiberApp.Use(middleware.OapiRequestValidator(swagger))
|
||||
|
||||
api.RegisterHandlers(fiberApp)
|
||||
|
||||
return fiberApp
|
||||
}
|
||||
|
||||
type ready struct {
|
||||
ready bool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (r *ready) setReady() {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
r.ready = true
|
||||
}
|
||||
|
||||
func (r *ready) isReady() bool {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
return r.ready
|
||||
}
|
||||
|
||||
func (r *ready) notReady() {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
r.ready = false
|
||||
}
|
||||
6
internal/version/version.go
Normal file
6
internal/version/version.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package version
|
||||
|
||||
var (
|
||||
Version = "dev"
|
||||
Commit = "N/A"
|
||||
)
|
||||
Reference in a new issue