switch to pgx driver + create merch
This commit is contained in:
parent
546fe13107
commit
97f8d27430
13 changed files with 289 additions and 69 deletions
|
|
@ -2,13 +2,14 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"merch-api/config"
|
"merch-api/config"
|
||||||
_ "merch-api/docs"
|
_ "merch-api/docs"
|
||||||
"merch-api/internal/app"
|
"merch-api/internal/app"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @Title Merch API
|
// @Title Merch API
|
||||||
|
|
@ -22,7 +23,7 @@ func main() {
|
||||||
cfg := config.NewConfig()
|
cfg := config.NewConfig()
|
||||||
config.LogSetup(cfg.App.Mode, cfg.App.LogLvl)
|
config.LogSetup(cfg.App.Mode, cfg.App.LogLvl)
|
||||||
|
|
||||||
appl := app.New(cfg)
|
appl := app.New(ctx, cfg)
|
||||||
|
|
||||||
if err := appl.Run(ctx); err != nil {
|
if err := appl.Run(ctx); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,17 @@ package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"merch-api/config"
|
"merch-api/config"
|
||||||
"merch-api/internal/merch"
|
"merch-api/internal/merch"
|
||||||
|
"merch-api/internal/user"
|
||||||
"merch-api/pkg/dbase"
|
"merch-api/pkg/dbase"
|
||||||
"merch-api/pkg/router"
|
"merch-api/pkg/router"
|
||||||
"merch-api/pkg/utils"
|
"merch-api/pkg/utils"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pkgLogHeader string = "Application |"
|
const pkgLogHeader string = "Application |"
|
||||||
|
|
@ -18,9 +21,10 @@ type App struct {
|
||||||
cfg config.Config
|
cfg config.Config
|
||||||
router *router.Router
|
router *router.Router
|
||||||
modules []Module
|
modules []Module
|
||||||
|
dbPool *pgxpool.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg config.Config) *App {
|
func New(ctx context.Context, cfg config.Config) *App {
|
||||||
//providers
|
//providers
|
||||||
r := router.NewRouter(router.Deps{
|
r := router.NewRouter(router.Deps{
|
||||||
Host: cfg.Http.Host,
|
Host: cfg.Http.Host,
|
||||||
|
|
@ -29,7 +33,7 @@ func New(cfg config.Config) *App {
|
||||||
GinMode: cfg.Http.GinMode,
|
GinMode: cfg.Http.GinMode,
|
||||||
})
|
})
|
||||||
|
|
||||||
db, err := dbase.Connect(dbase.Deps{
|
dbPool, err := dbase.ConnectPool(ctx, dbase.Deps{
|
||||||
Host: cfg.DBase.Host,
|
Host: cfg.DBase.Host,
|
||||||
Port: cfg.DBase.Port,
|
Port: cfg.DBase.Port,
|
||||||
Username: cfg.DBase.Username,
|
Username: cfg.DBase.Username,
|
||||||
|
|
@ -39,6 +43,11 @@ func New(cfg config.Config) *App {
|
||||||
|
|
||||||
u := utils.New()
|
u := utils.New()
|
||||||
|
|
||||||
|
userProv := user.New(user.Deps{
|
||||||
|
DB: dbPool,
|
||||||
|
Utils: u,
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Fatalf("%v failed to connect database", pkgLogHeader)
|
log.WithError(err).Fatalf("%v failed to connect database", pkgLogHeader)
|
||||||
}
|
}
|
||||||
|
|
@ -47,8 +56,9 @@ func New(cfg config.Config) *App {
|
||||||
var modules []Module
|
var modules []Module
|
||||||
|
|
||||||
m := merch.New(merch.Deps{
|
m := merch.New(merch.Deps{
|
||||||
DB: db,
|
DB: dbPool,
|
||||||
Utils: u,
|
Utils: u,
|
||||||
|
UserProvider: userProv,
|
||||||
})
|
})
|
||||||
modules = append(modules, m)
|
modules = append(modules, m)
|
||||||
|
|
||||||
|
|
@ -56,6 +66,7 @@ func New(cfg config.Config) *App {
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
router: r,
|
router: r,
|
||||||
modules: modules,
|
modules: modules,
|
||||||
|
dbPool: dbPool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,6 +99,8 @@ func (app *App) shutdown(ctx context.Context) {
|
||||||
shutdownCtx, shutdownCancel := context.WithTimeout(ctx, 15*time.Second)
|
shutdownCtx, shutdownCancel := context.WithTimeout(ctx, 15*time.Second)
|
||||||
defer shutdownCancel()
|
defer shutdownCancel()
|
||||||
|
|
||||||
|
app.dbPool.Close()
|
||||||
|
|
||||||
if err := app.router.Shutdown(shutdownCtx); err != nil {
|
if err := app.router.Shutdown(shutdownCtx); err != nil {
|
||||||
log.WithError(err).Warnf("%v error shutting down application", pkgLogHeader)
|
log.WithError(err).Warnf("%v error shutting down application", pkgLogHeader)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,19 @@ package merch
|
||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"merch-api/pkg/responses"
|
"merch-api/pkg/responses"
|
||||||
|
"merch-api/pkg/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type controller struct {
|
type controller struct {
|
||||||
service *service
|
service *service
|
||||||
|
utils utils.Utils
|
||||||
}
|
}
|
||||||
|
|
||||||
func newController(s *service) *controller {
|
func newController(s *service, u utils.Utils) *controller {
|
||||||
return &controller{
|
return &controller{
|
||||||
service: s,
|
service: s,
|
||||||
|
utils: u,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,7 +35,41 @@ func (h *Handler) RegisterRoutes(r *gin.RouterGroup) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *controller) create(c *gin.Context) {}
|
// create godoc
|
||||||
|
//
|
||||||
|
// @Summary Create new merch
|
||||||
|
// @Description Create new merch
|
||||||
|
// @Tags Merch
|
||||||
|
// @Accept json
|
||||||
|
// @Param merch body newMerchDTO true "merch body"
|
||||||
|
// @Success 201
|
||||||
|
// @Failure 400 {object} responses.BadRequest
|
||||||
|
// @Failure 401 {object} responses.Unauthorized
|
||||||
|
// @Failure 500 {object} responses.InternalServerError
|
||||||
|
// @Router /merch/create [POST]
|
||||||
|
func (co *controller) create(c *gin.Context) {
|
||||||
|
userUuid, err := co.utils.GetUserUuidFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, responses.Unauthorized{Error: err.Error()})
|
||||||
|
logErrController(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var newMerch newMerchDTO
|
||||||
|
if err = c.ShouldBindJSON(&newMerch); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, responses.BadRequest{Error: err.Error()})
|
||||||
|
logErrController(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = co.service.createMerch(c, userUuid, &newMerch); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, responses.InternalServerError{Error: err.Error()})
|
||||||
|
logErrController(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Status(http.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
func (co *controller) getOne(c *gin.Context) {}
|
func (co *controller) getOne(c *gin.Context) {}
|
||||||
|
|
||||||
|
|
@ -63,7 +100,7 @@ func (co *controller) createOrigin(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := co.service.createOrigin(origin); err != nil {
|
if err := co.service.createOrigin(c, origin); err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, responses.InternalServerError{Error: err.Error()})
|
c.JSON(http.StatusInternalServerError, responses.InternalServerError{Error: err.Error()})
|
||||||
logErrController(err)
|
logErrController(err)
|
||||||
return
|
return
|
||||||
|
|
@ -86,7 +123,7 @@ func (co *controller) createOrigin(c *gin.Context) {
|
||||||
// @Failure 500 {object} responses.InternalServerError
|
// @Failure 500 {object} responses.InternalServerError
|
||||||
// @Router /merch/origins [GET]
|
// @Router /merch/origins [GET]
|
||||||
func (co *controller) getOrigins(c *gin.Context) {
|
func (co *controller) getOrigins(c *gin.Context) {
|
||||||
response, err := co.service.getOrigins()
|
response, err := co.service.getOrigins(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, responses.InternalServerError{Error: err.Error()})
|
c.JSON(http.StatusInternalServerError, responses.InternalServerError{Error: err.Error()})
|
||||||
logErrController(err)
|
logErrController(err)
|
||||||
|
|
@ -118,7 +155,7 @@ func (co *controller) deleteOrigin(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := co.service.deleteOrigin(origin); err != nil {
|
if err := co.service.deleteOrigin(c, origin); err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, responses.InternalServerError{Error: err.Error()})
|
c.JSON(http.StatusInternalServerError, responses.InternalServerError{Error: err.Error()})
|
||||||
logErrController(err)
|
logErrController(err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package merch
|
package merch
|
||||||
|
|
||||||
|
// Origins
|
||||||
type newOriginDTO struct {
|
type newOriginDTO struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
@ -7,10 +8,21 @@ type originsDTO struct {
|
||||||
Origins []originItem `json:"origins"`
|
Origins []originItem `json:"origins"`
|
||||||
}
|
}
|
||||||
type originItem struct {
|
type originItem struct {
|
||||||
Id uint `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type deleteOriginDTO struct {
|
type deleteOriginDTO struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merch
|
||||||
|
type newMerchDTO struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Links []originLink `json:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type originLink struct {
|
||||||
|
Name string `json:"origin_name"`
|
||||||
|
Link string `json:"origin_link"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
package merch
|
package merch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
"merch-api/internal/user"
|
||||||
"merch-api/pkg/utils"
|
"merch-api/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -10,14 +11,15 @@ type Handler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Deps struct {
|
type Deps struct {
|
||||||
DB *sql.DB
|
DB *pgxpool.Pool
|
||||||
Utils utils.Utils
|
Utils utils.Utils
|
||||||
|
UserProvider user.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(deps Deps) *Handler {
|
func New(deps Deps) *Handler {
|
||||||
r := newRepo(deps.DB)
|
r := newRepo(deps.DB)
|
||||||
s := newService(r, deps.Utils)
|
s := newService(r, deps.Utils, deps.UserProvider)
|
||||||
c := newController(s)
|
c := newController(s, deps.Utils)
|
||||||
|
|
||||||
return &Handler{
|
return &Handler{
|
||||||
controller: c,
|
controller: c,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Merch struct {
|
type Merch struct {
|
||||||
Id uint
|
Id int64
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt sql.NullTime
|
UpdatedAt sql.NullTime
|
||||||
DeletedAt sql.NullTime
|
DeletedAt sql.NullTime
|
||||||
|
|
@ -17,7 +17,7 @@ type Merch struct {
|
||||||
|
|
||||||
// Origin model. Table name: merch_origins
|
// Origin model. Table name: merch_origins
|
||||||
type Origin struct {
|
type Origin struct {
|
||||||
Id uint
|
Id int64
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
DeletedAt sql.NullTime
|
DeletedAt sql.NullTime
|
||||||
Name string
|
Name string
|
||||||
|
|
@ -25,11 +25,22 @@ type Origin struct {
|
||||||
|
|
||||||
// Price model. Table name: merch_prices
|
// Price model. Table name: merch_prices
|
||||||
type Price struct {
|
type Price struct {
|
||||||
Id uint
|
Id int64
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt sql.NullTime
|
UpdatedAt sql.NullTime
|
||||||
DeletedAt sql.NullTime
|
DeletedAt sql.NullTime
|
||||||
MerchUuid string
|
MerchUuid string
|
||||||
Price int
|
Price int
|
||||||
OriginId uint
|
OriginId int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtraData model. Table name: merch_extra_data
|
||||||
|
type ExtraData struct {
|
||||||
|
Id int64
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt sql.NullTime
|
||||||
|
DeletedAt sql.NullTime
|
||||||
|
MerchId int64
|
||||||
|
OriginId int64
|
||||||
|
URL string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,39 @@
|
||||||
package merch
|
package merch
|
||||||
|
|
||||||
import "database/sql"
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
|
createMerch(ctx context.Context, merch *Merch, extra []ExtraData) error
|
||||||
|
|
||||||
Origins
|
Origins
|
||||||
}
|
}
|
||||||
|
|
||||||
type Origins interface {
|
type Origins interface {
|
||||||
createOrigin(origin *Origin) error
|
createOrigin(ctx context.Context, origin *Origin) error
|
||||||
getOrigins() ([]Origin, error)
|
getOrigins(ctx context.Context) ([]Origin, error)
|
||||||
deleteOriginByName(name string, deletedAt sql.NullTime) error
|
deleteOriginByName(ctx context.Context, name string, deletedAt sql.NullTime) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type repo struct {
|
type repo struct {
|
||||||
db *sql.DB
|
db *pgxpool.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRepo(db *sql.DB) Repository {
|
func newRepo(db *pgxpool.Pool) Repository {
|
||||||
return &repo{
|
return &repo{
|
||||||
db: db,
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repo) createOrigin(origin *Origin) error {
|
func (r *repo) createOrigin(ctx context.Context, origin *Origin) error {
|
||||||
q := `INSERT INTO merch_origins (created_at, deleted_at, name) VALUES ($1, $2, $3)`
|
q := `INSERT INTO merch_origins (created_at, deleted_at, name) VALUES ($1, $2, $3)`
|
||||||
|
|
||||||
_, err := r.db.Exec(q, origin.CreatedAt, origin.DeletedAt, origin.Name)
|
_, err := r.db.Exec(ctx, q, origin.CreatedAt, origin.DeletedAt, origin.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -33,10 +41,10 @@ func (r *repo) createOrigin(origin *Origin) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repo) getOrigins() ([]Origin, error) {
|
func (r *repo) getOrigins(ctx context.Context) ([]Origin, error) {
|
||||||
q := `SELECT * FROM merch_origins WHERE deleted_at IS NULL`
|
q := `SELECT * FROM merch_origins WHERE deleted_at IS NULL`
|
||||||
|
|
||||||
rows, err := r.db.Query(q)
|
rows, err := r.db.Query(ctx, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -58,13 +66,63 @@ func (r *repo) getOrigins() ([]Origin, error) {
|
||||||
return origins, nil
|
return origins, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repo) deleteOriginByName(name string, deletedAt sql.NullTime) error {
|
func (r *repo) deleteOriginByName(ctx context.Context, name string, deletedAt sql.NullTime) error {
|
||||||
q := `UPDATE merch_origins SET deleted_at = $1 WHERE name = $2`
|
q := `UPDATE merch_origins SET deleted_at = $1 WHERE name = $2`
|
||||||
|
|
||||||
_, err := r.db.Exec(q, deletedAt.Time, name)
|
_, err := r.db.Exec(ctx, q, deletedAt.Time, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Merch crud
|
||||||
|
|
||||||
|
func (r *repo) createMerch(ctx context.Context, merch *Merch, extra []ExtraData) error {
|
||||||
|
tx, err := r.db.Begin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
qMerch := `INSERT INTO merch (created_at, updated_at, deleted_at, merch_uuid, user_id, name)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6) RETURNING id`
|
||||||
|
|
||||||
|
var merchId int64
|
||||||
|
err = tx.
|
||||||
|
QueryRow(ctx, qMerch, merch.CreatedAt, merch.UpdatedAt, merch.DeletedAt, merch.MerchUuid, merch.UserId, merch.Name).
|
||||||
|
Scan(&merchId)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if extra == nil {
|
||||||
|
return tx.Commit(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
countArgs := 1
|
||||||
|
var insertFields []string
|
||||||
|
var insertArgs []interface{}
|
||||||
|
|
||||||
|
for _, item := range extra {
|
||||||
|
insertFields = append(insertFields, fmt.Sprintf("($%v, $%v, $%v, $%v, $%v, $%v)",
|
||||||
|
countArgs, countArgs+1, countArgs+2, countArgs+3, countArgs+4, countArgs+5))
|
||||||
|
|
||||||
|
insertArgs = append(insertArgs, item.CreatedAt, item.UpdatedAt, item.DeletedAt, merchId, item.OriginId, item.URL)
|
||||||
|
|
||||||
|
countArgs += 6
|
||||||
|
}
|
||||||
|
|
||||||
|
qExtra := fmt.Sprintf(
|
||||||
|
"INSERT INTO merch_extra_data (created_at, updated_at, deleted_at, merch_id, origin_id, url) VALUES %v",
|
||||||
|
strings.Join(insertFields, ","))
|
||||||
|
|
||||||
|
_, err = tx.Exec(ctx, qExtra, insertArgs...)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Commit(ctx)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,30 @@
|
||||||
package merch
|
package merch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"merch-api/internal/user"
|
||||||
"merch-api/pkg/utils"
|
"merch-api/pkg/utils"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
repo Repository
|
repo Repository
|
||||||
utils utils.Utils
|
utils utils.Utils
|
||||||
|
userProvider user.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
func newService(repo Repository, u utils.Utils) *service {
|
func newService(repo Repository, u utils.Utils, up user.Provider) *service {
|
||||||
return &service{
|
return &service{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
utils: u,
|
utils: u,
|
||||||
|
userProvider: up,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) createOrigin(o *newOriginDTO) error {
|
//origins
|
||||||
|
|
||||||
|
func (s *service) createOrigin(ctx context.Context, o *newOriginDTO) error {
|
||||||
newOrigin := &Origin{
|
newOrigin := &Origin{
|
||||||
CreatedAt: s.utils.TimeNowUTC(),
|
CreatedAt: s.utils.TimeNowUTC(),
|
||||||
DeletedAt: s.utils.DeletedNullTime(),
|
DeletedAt: s.utils.DeletedNullTime(),
|
||||||
|
|
@ -24,11 +32,11 @@ func (s *service) createOrigin(o *newOriginDTO) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
logDebugService("create origin success")
|
logDebugService("create origin success")
|
||||||
return s.repo.createOrigin(newOrigin)
|
return s.repo.createOrigin(ctx, newOrigin)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) getOrigins() (*originsDTO, error) {
|
func (s *service) getOrigins(ctx context.Context) (*originsDTO, error) {
|
||||||
data, err := s.repo.getOrigins()
|
data, err := s.repo.getOrigins(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logErrService(err)
|
logErrService(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -50,7 +58,74 @@ func (s *service) getOrigins() (*originsDTO, error) {
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) deleteOrigin(origin *deleteOriginDTO) error {
|
func (s *service) deleteOrigin(ctx context.Context, origin *deleteOriginDTO) error {
|
||||||
logDebugService("delete origin success")
|
logDebugService("delete origin success")
|
||||||
return s.repo.deleteOriginByName(origin.Name, s.utils.NullTimeNowUTC())
|
return s.repo.deleteOriginByName(ctx, origin.Name, s.utils.NullTimeNowUTC())
|
||||||
|
}
|
||||||
|
|
||||||
|
// merch
|
||||||
|
|
||||||
|
func (s *service) createMerch(ctx context.Context, userUuid string, payload *newMerchDTO) error {
|
||||||
|
userId, err := s.userProvider.GetUserId(ctx, userUuid)
|
||||||
|
if err != nil {
|
||||||
|
logErrService(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
now := s.utils.TimeNowUTC()
|
||||||
|
nullNow := s.utils.NullTimeFromNow(now)
|
||||||
|
empty := s.utils.DeletedNullTime()
|
||||||
|
|
||||||
|
merchUuid, err := uuid.NewV7()
|
||||||
|
if err != nil {
|
||||||
|
logErrService(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newMerch := &Merch{
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: nullNow,
|
||||||
|
DeletedAt: empty,
|
||||||
|
MerchUuid: merchUuid.String(),
|
||||||
|
UserId: userId,
|
||||||
|
Name: payload.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
var merchExtra []ExtraData
|
||||||
|
if payload.Links != nil {
|
||||||
|
|
||||||
|
originsMap, err := s.getOriginsMap(ctx)
|
||||||
|
if err != nil {
|
||||||
|
logErrService(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range payload.Links {
|
||||||
|
merchExtra = append(merchExtra, ExtraData{
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: nullNow,
|
||||||
|
DeletedAt: empty,
|
||||||
|
MerchId: 0,
|
||||||
|
OriginId: originsMap[item.Name],
|
||||||
|
URL: item.Link,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.repo.createMerch(ctx, newMerch, merchExtra)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) getOriginsMap(ctx context.Context) (map[string]int64, error) {
|
||||||
|
origins, err := s.repo.getOrigins(ctx)
|
||||||
|
if err != nil {
|
||||||
|
logErrService(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
originsMap := make(map[string]int64, len(origins))
|
||||||
|
for _, origin := range origins {
|
||||||
|
originsMap[origin.Name] = origin.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
return originsMap, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"merch-api/pkg/utils"
|
"merch-api/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@ type Handler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Deps struct {
|
type Deps struct {
|
||||||
DB *sql.DB
|
DB *pgxpool.Pool
|
||||||
Utils utils.Utils
|
Utils utils.Utils
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package user
|
package user
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
GetUserId(userUuid string) (string, error)
|
GetUserId(ctx context.Context, userUuid string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,27 @@
|
||||||
package user
|
package user
|
||||||
|
|
||||||
import "database/sql"
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
)
|
||||||
|
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
getUserId(userUuid string) (string, error)
|
getUserId(ctx context.Context, userUuid string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type repo struct {
|
type repo struct {
|
||||||
db *sql.DB
|
db *pgxpool.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRepository(db *sql.DB) Repository {
|
func newRepository(db *pgxpool.Pool) Repository {
|
||||||
return &repo{
|
return &repo{
|
||||||
db: db,
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repo) getUserId(userUuid string) (string, error) {
|
func (r *repo) getUserId(ctx context.Context, userUuid string) (string, error) {
|
||||||
q := `SELECT id FROM users WHERE uuid = $1 AND deleted_at IS NULL LIMIT 1`
|
q := `SELECT id FROM users WHERE uuid = $1 AND deleted_at IS NULL LIMIT 1`
|
||||||
row := r.db.QueryRow(q, userUuid)
|
row := r.db.QueryRow(ctx, q, userUuid)
|
||||||
|
|
||||||
var id string
|
var id string
|
||||||
if err := row.Scan(&id); err != nil {
|
if err := row.Scan(&id); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"merch-api/pkg/utils"
|
"merch-api/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
@ -17,10 +18,10 @@ func newService(repo Repository, utils utils.Utils) *service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) GetUserId(userUuid string) (string, error) {
|
func (s *service) GetUserId(ctx context.Context, userUuid string) (string, error) {
|
||||||
if userUuid == "" {
|
if userUuid == "" {
|
||||||
return "", errors.New("user uuid is empty")
|
return "", errors.New("user uuid is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.repo.getUserId(userUuid)
|
return s.repo.getUserId(ctx, userUuid)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
package dbase
|
package dbase
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/jackc/pgx"
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Deps struct {
|
type Deps struct {
|
||||||
|
|
@ -14,20 +15,24 @@ type Deps struct {
|
||||||
DBName string
|
DBName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Connect(deps Deps) (*sql.DB, error) {
|
func ConnectPool(ctx context.Context, deps Deps) (*pgxpool.Pool, error) {
|
||||||
dsn := fmt.Sprintf(
|
connString := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s",
|
||||||
"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
|
|
||||||
deps.Host, deps.Port, deps.Username, deps.Password, deps.DBName)
|
deps.Host, deps.Port, deps.Username, deps.Password, deps.DBName)
|
||||||
|
|
||||||
db, err := sql.Open("postgres", dsn)
|
config, err := pgxpool.ParseConfig(connString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = db.Ping(); err != nil {
|
pool, err := pgxpool.NewWithConfig(ctx, config)
|
||||||
db.Close()
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to connect to DB: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return db, nil
|
if err = pool.Ping(ctx); err != nil {
|
||||||
|
pool.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue