1
0
Fork 0

feat: add postgres repository for training

This commit is contained in:
Vojtěch Mareš 2024-06-23 10:35:38 +02:00
parent 8c277ef692
commit ff7e320481
Signed by: vojtech.mares
GPG key ID: C6827B976F17240D
19 changed files with 1277 additions and 414 deletions

View file

@ -0,0 +1,95 @@
package bootstrap
import (
"context"
pgxDeciaml "github.com/jackc/pgx-shopspring-decimal"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
pgxUUID "github.com/vgarvardt/pgx-google-uuid/v5"
"gitlab.mareshq.com/hq/yggdrasil/internal/faker"
"gitlab.mareshq.com/hq/yggdrasil/internal/server"
"gitlab.mareshq.com/hq/yggdrasil/pkg/training"
"go.uber.org/zap"
"os"
"strconv"
"strings"
)
func Bootstrap(logger *zap.Logger) *server.Server {
portStr := os.Getenv("APP_PORT")
if portStr == "" {
logger.Fatal("APP_PORT is not set")
}
port, err := strconv.Atoi(portStr)
if err != nil {
logger.Fatal("Error parsing APP_PORT", zap.Error(err))
}
databaseURLFile := os.Getenv("DATABASE_URL_FILE")
if databaseURLFile == "" {
logger.Fatal("DATABASE_URL_FILE is not set")
}
dat, err := os.ReadFile(databaseURLFile)
if err != nil {
logger.Fatal("Error reading DATABASE_URL_FILE", zap.Error(err))
}
databaseURL := string(dat)
// clean up url from invalid characters
databaseURL = strings.ReplaceAll(databaseURL, "\n", "")
databaseURL = strings.ReplaceAll(databaseURL, "\t", "")
pgxConfig, err := pgxpool.ParseConfig(databaseURL)
if err != nil {
logger.Fatal("Error parsing database url with pgx (database driver)", zap.Error(err))
}
pgxConfig.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
pgxUUID.Register(conn.TypeMap())
pgxDeciaml.Register(conn.TypeMap())
return nil
}
pool, err := pgxpool.NewWithConfig(context.TODO(), pgxConfig)
if err != nil {
logger.Fatal("Error creating pgx pool", zap.Error(err))
}
trainingRepo := training.NewPostgresTrainingRepository(pool)
trainingDateRepo := training.NewPostgresTrainingDateRepository(pool)
trainingDateAttendeeRepo := training.NewPostgresTrainingDateAttendeeRepository(pool)
apiHandlers := server.NewAPIHandlers(trainingRepo, trainingDateRepo, trainingDateAttendeeRepo)
srv := server.NewServer(apiHandlers, port, logger, pool)
return srv
}
func BootstrapInMemory(logger *zap.Logger) *server.Server {
portStr := os.Getenv("APP_PORT")
if portStr == "" {
logger.Fatal("APP_PORT is not set")
}
port, err := strconv.Atoi(portStr)
if err != nil {
logger.Fatal("Error parsing APP_PORT", zap.Error(err))
}
trainingRepository := training.NewInMemoryTrainingRepository()
trainingDateRepository := training.NewInMemoryTrainingDateRepository()
trainingDateAttendeeRepository := training.NewInMemoryTrainingDateAttendeeRepository()
f := faker.NewFaker(trainingRepository, trainingDateRepository)
if err := f.GenerateFakeData(); err != nil {
logger.Fatal("Error generating fake data", zap.Error(err))
}
apiHandlers := server.NewAPIHandlers(trainingRepository, trainingDateRepository, trainingDateAttendeeRepository)
srv := server.NewServer(apiHandlers, port, logger, nil)
return srv
}

View file

@ -0,0 +1,23 @@
package bootstrap
import (
"go.uber.org/zap"
"os"
)
func Logger() *zap.Logger {
if os.Getenv("APP_ENV") == "development" {
logger, err := zap.NewDevelopment()
if err != nil {
return zap.NewNop()
}
return logger
}
logger, err := zap.NewProduction()
if err != nil {
return zap.NewNop()
}
return logger
}

View file

@ -207,11 +207,11 @@ func (f *Faker) GenerateFakeData() error {
}
now := time.Now()
min := time.Date(now.Year(), now.Month(), now.Day(), 8, 0, 0, 0, time.UTC)
max := time.Date(now.Year()+1, now.Month(), now.Day(), 8, 0, 0, 0, time.UTC)
delta := max.Sub(min)
minT := time.Date(now.Year(), now.Month(), now.Day(), 8, 0, 0, 0, time.UTC)
maxT := time.Date(now.Year()+1, now.Month(), now.Day(), 8, 0, 0, 0, time.UTC)
delta := maxT.Sub(minT)
sec := rand.Int64N(int64(delta.Seconds())) + min.UnixNano()/1000000000
sec := rand.Int64N(int64(delta.Seconds())) + minT.UnixNano()/1000000000
date := time.Unix(sec, 0)
amount := decimal.NewFromInt(4900)
@ -231,21 +231,19 @@ func (f *Faker) GenerateFakeData() error {
}
td := training.TrainingDate{
Date: date,
StartTime: date.Truncate(24 * time.Hour),
Days: t.Days,
IsOnline: online,
Location: location,
Address: "TBD",
Capacity: 12,
Price: money.Price{
Amount: amount,
Currency: cur,
},
Date: date,
StartTime: date.Truncate(24 * time.Hour),
Days: t.Days,
IsOnline: online,
Location: location,
Address: "TBD",
Capacity: 12,
PriceAmount: amount,
PriceCurrency: cur,
}
err := f.trainingDateRepository.Create(t.ID, &td)
if err != nil {
return err
dateErr := f.trainingDateRepository.Create(t.ID, &td)
if dateErr != nil {
return dateErr
}
}
}

8
internal/money/money.go Normal file
View file

@ -0,0 +1,8 @@
package money
import "github.com/shopspring/decimal"
type Money struct {
Amount decimal.Decimal `db:"amount"`
Currency Currency `db:"currency"`
}

View file

@ -1,8 +0,0 @@
package money
import "github.com/shopspring/decimal"
type Price struct {
Amount decimal.Decimal
Currency Currency
}

View file

@ -238,8 +238,8 @@ func (h *APIHandlers) ListTrainingDates(ctx context.Context, req ListTrainingDat
Address: td.Address,
Capacity: td.Capacity,
Price: Price{
Amount: td.Price.Amount.String(),
Currency: td.Price.Currency,
Amount: td.PriceAmount.String(),
Currency: td.PriceCurrency,
},
}
}
@ -258,7 +258,7 @@ func (h *APIHandlers) CreateTrainingDate(ctx context.Context, req CreateTraining
}}, nil
}
price := money.Price{
price := money.Money{
Amount: amount,
Currency: req.Body.Price.Currency,
}
@ -274,14 +274,15 @@ func (h *APIHandlers) CreateTrainingDate(ctx context.Context, req CreateTraining
}
td := training.TrainingDate{
Date: req.Body.Date.Time,
StartTime: startTime,
Days: req.Body.Days,
IsOnline: req.Body.IsOnline,
Location: req.Body.Location,
Address: req.Body.Address,
Capacity: req.Body.Capacity,
Price: price,
Date: req.Body.Date.Time,
StartTime: startTime,
Days: req.Body.Days,
IsOnline: req.Body.IsOnline,
Location: req.Body.Location,
Address: req.Body.Address,
Capacity: req.Body.Capacity,
PriceAmount: price.Amount,
PriceCurrency: price.Currency,
}
err = h.trainingDateRepository.Create(req.TrainingID, &td)
@ -304,8 +305,8 @@ func (h *APIHandlers) CreateTrainingDate(ctx context.Context, req CreateTraining
Address: td.Address,
Capacity: td.Capacity,
Price: Price{
Amount: td.Price.Amount.String(),
Currency: td.Price.Currency,
Amount: td.PriceAmount.String(),
Currency: td.PriceCurrency,
},
}, nil
}
@ -357,8 +358,8 @@ func (h *APIHandlers) GetTrainingDate(ctx context.Context, req GetTrainingDateRe
Address: td.Address,
Capacity: td.Capacity,
Price: Price{
Amount: td.Price.Amount.String(),
Currency: td.Price.Currency,
Amount: td.PriceAmount.String(),
Currency: td.PriceCurrency,
},
}, nil
}
@ -374,7 +375,7 @@ func (h *APIHandlers) UpdateTrainingDate(ctx context.Context, req UpdateTraining
}}, nil
}
price := money.Price{
price := money.Money{
Amount: amount,
Currency: req.Body.Price.Currency,
}
@ -390,15 +391,16 @@ func (h *APIHandlers) UpdateTrainingDate(ctx context.Context, req UpdateTraining
}
td := training.TrainingDate{
ID: req.TrainingDateID,
Date: req.Body.Date.Time,
StartTime: startTime,
Days: req.Body.Days,
IsOnline: req.Body.IsOnline,
Location: req.Body.Location,
Address: req.Body.Address,
Capacity: req.Body.Capacity,
Price: price,
ID: req.TrainingDateID,
Date: req.Body.Date.Time,
StartTime: startTime,
Days: req.Body.Days,
IsOnline: req.Body.IsOnline,
Location: req.Body.Location,
Address: req.Body.Address,
Capacity: req.Body.Capacity,
PriceAmount: price.Amount,
PriceCurrency: price.Currency,
}
err = h.trainingDateRepository.Update(&td)
@ -421,8 +423,8 @@ func (h *APIHandlers) UpdateTrainingDate(ctx context.Context, req UpdateTraining
Address: td.Address,
Capacity: td.Capacity,
Price: Price{
Amount: td.Price.Amount.String(),
Currency: td.Price.Currency,
Amount: td.PriceAmount.String(),
Currency: td.PriceCurrency,
},
}, nil
}
@ -463,8 +465,8 @@ func (h *APIHandlers) ListAllUpcomingTrainingDates(ctx context.Context, req List
Address: td.Address,
Capacity: td.Capacity,
Price: Price{
Amount: td.Price.Amount.String(),
Currency: td.Price.Currency,
Amount: td.PriceAmount.String(),
Currency: td.PriceCurrency,
},
}
}
@ -496,8 +498,8 @@ func (h *APIHandlers) ListTrainingUpcomingDates(ctx context.Context, req ListTra
Address: td.Address,
Capacity: td.Capacity,
Price: Price{
Amount: td.Price.Amount.String(),
Currency: td.Price.Currency,
Amount: td.PriceAmount.String(),
Currency: td.PriceCurrency,
},
}
}
@ -529,8 +531,8 @@ func (h *APIHandlers) ListTrainingDateAttendees(ctx context.Context, req ListTra
HasPaid: a.HasPaid,
HasAttended: a.HasAttended,
Bill: Price{
Amount: a.Bill.Amount.String(),
Currency: a.Bill.Currency,
Amount: a.BillAmount.String(),
Currency: a.BillCurrency,
},
}
}
@ -576,15 +578,16 @@ func (h *APIHandlers) CreateTrainingDateAttendee(ctx context.Context, req Create
}
ta := training.TrainingDateAttendee{
Name: req.Body.Name,
Email: string(req.Body.Email),
Phone: req.Body.Phone,
Company: req.Body.Company,
Position: req.Body.Position,
IsStudent: *req.Body.IsStudent,
HasPaid: false,
HasAttended: false,
Bill: td.Price,
Name: req.Body.Name,
Email: string(req.Body.Email),
Phone: req.Body.Phone,
Company: req.Body.Company,
Position: req.Body.Position,
IsStudent: *req.Body.IsStudent,
HasPaid: false,
HasAttended: false,
BillAmount: td.PriceAmount,
BillCurrency: td.PriceCurrency,
}
err = h.trainingDateAttendeeRepository.Create(req.TrainingDateID, &ta)
@ -608,8 +611,8 @@ func (h *APIHandlers) CreateTrainingDateAttendee(ctx context.Context, req Create
HasAttended: ta.HasAttended,
HasPaid: ta.HasPaid,
Bill: Price{
Amount: ta.Bill.Amount.String(),
Currency: ta.Bill.Currency,
Amount: ta.BillAmount.String(),
Currency: ta.BillCurrency,
},
}, nil
}
@ -714,8 +717,8 @@ func (h *APIHandlers) GetTrainingDateAttendee(ctx context.Context, req GetTraini
HasAttended: ta.HasAttended,
HasPaid: ta.HasPaid,
Bill: Price{
Amount: ta.Bill.Amount.String(),
Currency: ta.Bill.Currency,
Amount: ta.BillAmount.String(),
Currency: ta.BillCurrency,
},
}, nil
}
@ -803,8 +806,8 @@ func (h *APIHandlers) UpdateTrainingDateAttendee(ctx context.Context, req Update
HasAttended: ta.HasAttended,
HasPaid: ta.HasPaid,
Bill: Price{
Amount: ta.Bill.Amount.String(),
Currency: ta.Bill.Currency,
Amount: ta.BillAmount.String(),
Currency: ta.BillCurrency,
},
}, nil
}
@ -887,8 +890,8 @@ func (h *APIHandlers) UpdateTrainingDateAttendeePayment(ctx context.Context, req
HasAttended: ta.HasAttended,
HasPaid: ta.HasPaid,
Bill: Price{
Amount: ta.Bill.Amount.String(),
Currency: ta.Bill.Currency,
Amount: ta.BillAmount.String(),
Currency: ta.BillCurrency,
},
}, nil
}
@ -971,8 +974,8 @@ func (h *APIHandlers) UpdateTrainingDateAttendeeAttendance(ctx context.Context,
HasAttended: ta.HasAttended,
HasPaid: ta.HasPaid,
Bill: Price{
Amount: ta.Bill.Amount.String(),
Currency: ta.Bill.Currency,
Amount: ta.BillAmount.String(),
Currency: ta.BillCurrency,
},
}, nil
}

View file

@ -3,6 +3,7 @@ package server
import (
"context"
"fmt"
"github.com/jackc/pgx/v5/pgxpool"
"time"
fiberzap "github.com/gofiber/contrib/fiberzap/v2"
@ -16,13 +17,15 @@ type Server struct {
port int
logger *zap.Logger
apiHandlers *APIHandlers
pool *pgxpool.Pool
}
func NewServer(apiHandlers *APIHandlers, port int, logger *zap.Logger) *Server {
func NewServer(apiHandlers *APIHandlers, port int, logger *zap.Logger, pool *pgxpool.Pool) *Server {
return &Server{
apiHandlers: apiHandlers,
port: port,
logger: logger,
pool: pool,
}
}
@ -54,6 +57,11 @@ func (s *Server) Run(ctx context.Context) {
panic(err)
}
// gracefully shutdown/close database pool
if s.pool != nil {
s.pool.Close()
}
s.logger.Info("HTTP server shut down gracefully.", zap.Duration("duration", time.Since(shutdownBegan)))
}

View file

@ -338,7 +338,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -384,7 +384,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -447,7 +447,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -510,7 +510,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -584,7 +584,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -599,7 +599,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -614,7 +614,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -663,7 +663,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -705,7 +705,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -765,7 +765,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -782,7 +782,7 @@ func TestServer(t *testing.T) {
Position: "Software Engineer",
Phone: "+420 123 456 789",
IsStudent: false,
Bill: money.Price{
Bill: money.Money{
Amount: td.Price.Amount,
Currency: td.Price.Currency,
},
@ -842,7 +842,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -856,7 +856,7 @@ func TestServer(t *testing.T) {
Position: "Software Engineer",
Phone: "+420 123 456 789",
IsStudent: false,
Bill: money.Price{
Bill: money.Money{
Amount: td.Price.Amount,
Currency: td.Price.Currency,
},
@ -909,7 +909,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -923,7 +923,7 @@ func TestServer(t *testing.T) {
Position: "Software Engineer",
Phone: "+420 123 456 789",
IsStudent: false,
Bill: money.Price{
Bill: money.Money{
Amount: td.Price.Amount,
Currency: td.Price.Currency,
},
@ -986,7 +986,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -1000,7 +1000,7 @@ func TestServer(t *testing.T) {
Position: "Software Engineer",
Phone: "+420 123 456 789",
IsStudent: false,
Bill: money.Price{
Bill: money.Money{
Amount: td.Price.Amount,
Currency: td.Price.Currency,
},
@ -1046,7 +1046,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -1064,7 +1064,7 @@ func TestServer(t *testing.T) {
Position: "Software Engineer",
Phone: "+420 123 456 789",
IsStudent: false,
Bill: money.Price{
Bill: money.Money{
Amount: td.Price.Amount,
Currency: td.Price.Currency,
},
@ -1123,7 +1123,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -1141,7 +1141,7 @@ func TestServer(t *testing.T) {
Position: "Software Engineer",
Phone: "+420 123 456 789",
IsStudent: false,
Bill: money.Price{
Bill: money.Money{
Amount: td.Price.Amount,
Currency: td.Price.Currency,
},
@ -1200,7 +1200,7 @@ func TestServer(t *testing.T) {
IsOnline: false,
Location: "NYC",
StartTime: date,
Price: money.Price{
Price: money.Money{
Amount: decimal.NewFromInt(200),
Currency: "EUR",
},
@ -1216,7 +1216,7 @@ func TestServer(t *testing.T) {
Position: "Software Engineer",
Phone: "+420 123 456 789",
IsStudent: false,
Bill: money.Price{
Bill: money.Money{
Amount: td.Price.Amount,
Currency: td.Price.Currency,
},