From 180782e9a34919c893c1e115117816b571f48652 Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 19 May 2024 16:54:02 +0600 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D1=80=D1=83=D1=87=D0=BA=D1=83=20POST=20user/orders?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/app/client/pg/queries/gophermart.sql | 8 +++ .../gophermartprovider/check_user_order.go | 35 ++++++++++ .../app/gophermartprovider/create_order.go | 36 ++++++++++ .../app/gophermartprovider/create_user.go | 11 ++- .../gophermartprovider/gophermart_models.go | 8 ++- .../app/gophermartprovider/models/order.go | 8 +++ .../httpserver/middleware/access_control.go | 28 +++++++- internal/app/httpserver/server.go | 4 +- internal/app/httpserver/user/create_order.go | 67 +++++++++++++++++++ internal/app/httpserver/user/login.go | 4 +- internal/app/httpserver/user/router.go | 22 +++--- .../migration/migrations/0001__init_db.up.sql | 16 +++++ internal/app/usecase/auth/login.go | 10 +-- internal/app/usecase/auth/register.go | 4 +- internal/app/usecase/auth/usecase.go | 2 +- internal/app/usecase/order/create_order.go | 52 ++++++++++++++ internal/app/usecase/order/errors/errors.go | 15 +++++ internal/app/usecase/order/usecase.go | 27 ++++++++ internal/app/validator/algorithm_luhn.go | 42 ++++++++++++ internal/app/validator/user_create_order.go | 28 ++++++++ internal/app/validator/validator.go | 1 + internal/bootstrap/app.go | 3 + 22 files changed, 402 insertions(+), 29 deletions(-) create mode 100644 internal/app/gophermartprovider/check_user_order.go create mode 100644 internal/app/gophermartprovider/create_order.go create mode 100644 internal/app/gophermartprovider/models/order.go create mode 100644 internal/app/httpserver/user/create_order.go create mode 100644 internal/app/usecase/order/create_order.go create mode 100644 internal/app/usecase/order/errors/errors.go create mode 100644 internal/app/usecase/order/usecase.go create mode 100644 internal/app/validator/algorithm_luhn.go create mode 100644 internal/app/validator/user_create_order.go diff --git a/internal/app/client/pg/queries/gophermart.sql b/internal/app/client/pg/queries/gophermart.sql index b260b22..d671061 100644 --- a/internal/app/client/pg/queries/gophermart.sql +++ b/internal/app/client/pg/queries/gophermart.sql @@ -5,3 +5,11 @@ INSERT INTO gophermart.users (login, password) VALUES ($1, $2) ON CONFLICT (logi {{define "GetUserForLogin"}} SELECT id, login, password FROM gophermart.users WHERE login = $1; {{end}} + +{{define "CreateOrder"}} +INSERT INTO gophermart.orders (number, user_id) VALUES ($1, $2) ON CONFLICT (number) DO NOTHING RETURNING number; +{{end}} + +{{define "CheckUserOrder"}} +SELECT EXISTS(SELECT 1 FROM gophermart.orders WHERE number = $1 AND user_id = $2); +{{end}} diff --git a/internal/app/gophermartprovider/check_user_order.go b/internal/app/gophermartprovider/check_user_order.go new file mode 100644 index 0000000..4d86e27 --- /dev/null +++ b/internal/app/gophermartprovider/check_user_order.go @@ -0,0 +1,35 @@ +package gophermartprovider + +import ( + "context" + "fmt" + + "github.com/Froctnow/yandex-go-diploma/pkg/pgclient" +) + +func (p *GophermartDBProvider) CheckUserOrder( + ctx context.Context, + tx pgclient.Transaction, + orderNumber string, + userID string, +) (bool, error) { + rows, err := p.conn.NamedQueryxContext( + ctx, + "CheckUserOrder", + nil, + tx, + orderNumber, + userID, + ) + if err != nil { + return false, fmt.Errorf("can't execute CheckUserOrder: %w", err) + } + + err = rows.Err() + + if err != nil { + return false, fmt.Errorf("can't execute CheckUserOrder: %w", err) + } + + return pgclient.ValueFromRows[bool](rows) +} diff --git a/internal/app/gophermartprovider/create_order.go b/internal/app/gophermartprovider/create_order.go new file mode 100644 index 0000000..0b29186 --- /dev/null +++ b/internal/app/gophermartprovider/create_order.go @@ -0,0 +1,36 @@ +package gophermartprovider + +import ( + "context" + "fmt" + + "github.com/Froctnow/yandex-go-diploma/pkg/pgclient" +) + +// CreateOrder - создает новый заказ в базе данных и возвращает его номер +func (p *GophermartDBProvider) CreateOrder( + ctx context.Context, + tx pgclient.Transaction, + orderNumber string, + userID string, +) (string, error) { + rows, err := p.conn.NamedQueryxContext( + ctx, + "CreateOrder", + nil, + tx, + orderNumber, + userID, + ) + if err != nil { + return "", fmt.Errorf("can't execute CreateOrder: %w", err) + } + + err = rows.Err() + + if err != nil { + return "", fmt.Errorf("can't execute CreateOrder: %w", err) + } + + return pgclient.ValueFromRows[string](rows) +} diff --git a/internal/app/gophermartprovider/create_user.go b/internal/app/gophermartprovider/create_user.go index bac7064..c04a473 100644 --- a/internal/app/gophermartprovider/create_user.go +++ b/internal/app/gophermartprovider/create_user.go @@ -4,17 +4,16 @@ import ( "context" "fmt" - "github.com/Froctnow/yandex-go-diploma/internal/app/gophermartprovider/models" "github.com/Froctnow/yandex-go-diploma/pkg/pgclient" ) -// CreateUser - создает нового пользователя в базе данных и возвращает его +// CreateUser - создает нового пользователя в базе данных и возвращает его ID func (p *GophermartDBProvider) CreateUser( ctx context.Context, tx pgclient.Transaction, login string, password string, -) (models.User, error) { +) (string, error) { rows, err := p.conn.NamedQueryxContext( ctx, "CreateUser", @@ -24,14 +23,14 @@ func (p *GophermartDBProvider) CreateUser( password, ) if err != nil { - return models.User{}, fmt.Errorf("can't execute CreateUser: %w", err) + return "", fmt.Errorf("can't execute CreateUser: %w", err) } err = rows.Err() if err != nil { - return models.User{}, fmt.Errorf("can't execute CreateUser: %w", err) + return "", fmt.Errorf("can't execute CreateUser: %w", err) } - return pgclient.StructValueFromRows[models.User](rows) + return pgclient.ValueFromRows[string](rows) } diff --git a/internal/app/gophermartprovider/gophermart_models.go b/internal/app/gophermartprovider/gophermart_models.go index 7aecfec..0abe34b 100644 --- a/internal/app/gophermartprovider/gophermart_models.go +++ b/internal/app/gophermartprovider/gophermart_models.go @@ -13,8 +13,12 @@ type GophermartProvider interface { RollbackTransaction(tx pgclient.Transaction, log logger.LogClient) CommitTransaction(tx pgclient.Transaction) error - // CreateUser - создает нового пользователя в базе данных и возвращает его - CreateUser(ctx context.Context, tx pgclient.Transaction, login string, password string) (models.User, error) + // CreateUser - создает нового пользователя в базе данных и возвращает его ID + CreateUser(ctx context.Context, tx pgclient.Transaction, login string, password string) (string, error) // GetUserForLogin - возвращает пользователя по логину и паролю GetUserForLogin(ctx context.Context, tx pgclient.Transaction, login string) (models.User, error) + // CreateOrder - создает новый заказ в базе данных и возвращает его номер + CreateOrder(ctx context.Context, tx pgclient.Transaction, orderNumber string, userID string) (string, error) + // CheckUserOrder - проверят есть ли в базе номер заказа у пользователя + CheckUserOrder(ctx context.Context, tx pgclient.Transaction, orderNumber string, userID string) (bool, error) } diff --git a/internal/app/gophermartprovider/models/order.go b/internal/app/gophermartprovider/models/order.go new file mode 100644 index 0000000..6159adf --- /dev/null +++ b/internal/app/gophermartprovider/models/order.go @@ -0,0 +1,8 @@ +package models + +type Order struct { + Number string `db:"number"` + Status string `db:"status"` + Accrual *uint32 `db:"accrual"` + UploadedAt string `db:"uploaded_at"` +} diff --git a/internal/app/httpserver/middleware/access_control.go b/internal/app/httpserver/middleware/access_control.go index 534cec6..6683795 100644 --- a/internal/app/httpserver/middleware/access_control.go +++ b/internal/app/httpserver/middleware/access_control.go @@ -18,11 +18,24 @@ type Claims struct { UserID string `json:"user_id"` } +type TokenIsInvalid struct { +} + +func (e TokenIsInvalid) Error() string { + return "token is invalid" +} + func AccessControlMiddleware(cfg *config.Values, logger logger.LogClient) gin.HandlerFunc { return func(c *gin.Context) { jwtToken, err := c.Cookie("jwt") - if err != nil && !errors.Is(err, http.ErrNoCookie) { + if err != nil && errors.Is(err, http.ErrNoCookie) { + logger.WarnCtx(c, "jwt cookie is absent") + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + if err != nil { logger.ErrorCtx(c, fmt.Errorf("can't get jwt token from cookie: %w", err)) c.AbortWithStatus(http.StatusInternalServerError) return @@ -30,6 +43,12 @@ func AccessControlMiddleware(cfg *config.Values, logger logger.LogClient) gin.Ha decodedJwtToken, err := decodeJwtToken(jwtToken, cfg.JwtSecret) + if err != nil && errors.As(err, &TokenIsInvalid{}) { + logger.WarnCtx(c, "jwt token is invalid") + c.AbortWithStatus(http.StatusUnauthorized) + return + } + if err != nil { logger.ErrorCtx(c, fmt.Errorf("can't decode jwt token: %w", err)) c.AbortWithStatus(http.StatusInternalServerError) @@ -52,12 +71,17 @@ func decodeJwtToken(jwtToken string, jwtSecret string) (*Claims, error) { token, err := jwt.ParseWithClaims(jwtToken, &Claims{}, func(token *jwt.Token) (interface{}, error) { return []byte(jwtSecret), nil }) + + if err != nil && errors.Is(err, jwt.ErrSignatureInvalid) { + return nil, TokenIsInvalid{} + } + if err != nil { return nil, fmt.Errorf("can't parse token: %w", err) } if !token.Valid { - return nil, fmt.Errorf("token is invalid") + return nil, TokenIsInvalid{} } return token.Claims.(*Claims), nil diff --git a/internal/app/httpserver/server.go b/internal/app/httpserver/server.go index 79024eb..0a6c701 100644 --- a/internal/app/httpserver/server.go +++ b/internal/app/httpserver/server.go @@ -5,6 +5,7 @@ import ( "github.com/Froctnow/yandex-go-diploma/internal/app/httpserver/middleware" "github.com/Froctnow/yandex-go-diploma/internal/app/httpserver/user" "github.com/Froctnow/yandex-go-diploma/internal/app/usecase/auth" + "github.com/Froctnow/yandex-go-diploma/internal/app/usecase/order" "github.com/Froctnow/yandex-go-diploma/internal/app/validator" "github.com/Froctnow/yandex-go-diploma/pkg/logger" "github.com/gin-gonic/gin" @@ -23,6 +24,7 @@ func NewGophermartServer( validator validator.Validator, cfg *config.Values, authUseCase auth.UseCase, + orderUseCase order.UseCase, ) GophermartServer { ginEngine.Use(gin.Recovery()) @@ -32,6 +34,6 @@ func NewGophermartServer( apiGroup.Use(middleware.CompressMiddleware()) return &gophermartServer{ - user.NewRouter(apiGroup, authUseCase, validator, cfg, logger), + user.NewRouter(apiGroup, authUseCase, validator, cfg, logger, orderUseCase), } } diff --git a/internal/app/httpserver/user/create_order.go b/internal/app/httpserver/user/create_order.go new file mode 100644 index 0000000..d31319e --- /dev/null +++ b/internal/app/httpserver/user/create_order.go @@ -0,0 +1,67 @@ +package user + +import ( + "fmt" + "io" + "net/http" + "strings" + + "github.com/Froctnow/yandex-go-diploma/internal/app/httpserver/constants" + httpmodels "github.com/Froctnow/yandex-go-diploma/internal/app/httpserver/models" + ordererrors "github.com/Froctnow/yandex-go-diploma/internal/app/usecase/order/errors" + "github.com/gin-gonic/gin" + + "github.com/pkg/errors" +) + +func (r *userRouter) CreateOrder(ctx *gin.Context) { + headerContentType := ctx.GetHeader("Content-Type") + isCorrectHeaderContentType := checkHeaderContentType(headerContentType) + + if !isCorrectHeaderContentType { + ctx.AbortWithStatusJSON(http.StatusBadRequest, httpmodels.ErrorResponse{Error: constants.MessageErrorIncorrectContentType}) + return + } + + body, err := io.ReadAll(ctx.Request.Body) + + if err != nil { + ctx.AbortWithStatusJSON(http.StatusInternalServerError, httpmodels.ErrorResponse{Error: "Something went wrong"}) + return + } + + orderNumber := string(body) + + errs := r.validator.UserCreateOrder(orderNumber) + if len(errs.Errors) != 0 { + ctx.AbortWithStatusJSON(http.StatusUnprocessableEntity, httpmodels.ErrorResponse{Error: errs.Error()}) + return + } + + userID := ctx.GetString(constants.ContextUserID) + err = r.orderUseCase.Create(ctx, orderNumber, userID) + + if err != nil && errors.As(err, &ordererrors.UserOrderAlreadyExists{}) { + ctx.AbortWithStatus(http.StatusOK) + return + } + + if err != nil && errors.As(err, &ordererrors.OrderAlreadyExists{}) { + ctx.AbortWithStatus(http.StatusConflict) + return + } + + if err != nil { + r.logger.ErrorCtx(ctx, fmt.Errorf("failed create order")) + ctx.AbortWithStatusJSON(http.StatusInternalServerError, httpmodels.ErrorResponse{Error: err.Error()}) + return + } + + ctx.Status(http.StatusAccepted) +} + +func checkHeaderContentType(value string) bool { + isTextPlain := strings.Contains(value, "text/plain") + + return isTextPlain +} diff --git a/internal/app/httpserver/user/login.go b/internal/app/httpserver/user/login.go index 3890d6d..bb1d061 100644 --- a/internal/app/httpserver/user/login.go +++ b/internal/app/httpserver/user/login.go @@ -33,7 +33,7 @@ func (r *userRouter) Login(ctx *gin.Context) { return } - err := r.authUseCase.Login(ctx, req.Login, req.Password) + userID, err := r.authUseCase.Login(ctx, req.Login, req.Password) if err != nil && errors.As(err, &autherrors.IncorrectLoginPasswordError{}) { ctx.AbortWithStatus(http.StatusUnauthorized) @@ -46,7 +46,7 @@ func (r *userRouter) Login(ctx *gin.Context) { return } - err = r.authorizeUser(ctx, req.Login) + err = r.authorizeUser(ctx, userID) if err != nil { r.logger.ErrorCtx(ctx, fmt.Errorf("failed authorize user")) diff --git a/internal/app/httpserver/user/router.go b/internal/app/httpserver/user/router.go index b97e64a..60e00fa 100644 --- a/internal/app/httpserver/user/router.go +++ b/internal/app/httpserver/user/router.go @@ -7,7 +7,9 @@ import ( "github.com/Froctnow/yandex-go-diploma/internal/app/config" "github.com/Froctnow/yandex-go-diploma/internal/app/httpserver/constants" + "github.com/Froctnow/yandex-go-diploma/internal/app/httpserver/middleware" "github.com/Froctnow/yandex-go-diploma/internal/app/usecase/auth" + "github.com/Froctnow/yandex-go-diploma/internal/app/usecase/order" "github.com/Froctnow/yandex-go-diploma/internal/app/validator" "github.com/Froctnow/yandex-go-diploma/pkg/logger" "github.com/gin-gonic/gin" @@ -24,10 +26,11 @@ type Router interface { } type userRouter struct { - authUseCase auth.UseCase - validator validator.Validator - cfg *config.Values - logger logger.LogClient + authUseCase auth.UseCase + validator validator.Validator + cfg *config.Values + logger logger.LogClient + orderUseCase order.UseCase } func NewRouter( @@ -36,17 +39,20 @@ func NewRouter( validator validator.Validator, cfg *config.Values, logger logger.LogClient, + orderUseCase order.UseCase, ) Router { router := &userRouter{ - authUseCase: authUseCase, - validator: validator, - cfg: cfg, - logger: logger, + authUseCase: authUseCase, + validator: validator, + cfg: cfg, + logger: logger, + orderUseCase: orderUseCase, } userGroup := ginGroup.Group("/user") userGroup.POST("/register", router.Register) userGroup.POST("/login", router.Login) + userGroup.POST("/orders", middleware.AccessControlMiddleware(cfg, logger), router.CreateOrder) return router } diff --git a/internal/app/migration/migrations/0001__init_db.up.sql b/internal/app/migration/migrations/0001__init_db.up.sql index a452c0e..0aa4cdf 100644 --- a/internal/app/migration/migrations/0001__init_db.up.sql +++ b/internal/app/migration/migrations/0001__init_db.up.sql @@ -7,3 +7,19 @@ CREATE TABLE gophermart.users password TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ); + +CREATE TYPE gophermart.order_status AS ENUM ( + 'NEW', + 'PROCESSING', + 'INVALID', + 'PROCESSED' + ); + +CREATE TABLE gophermart.orders +( + number TEXT PRIMARY KEY, + user_id uuid NOT NULL REFERENCES gophermart.users (id), + accrual INTEGER, + status gophermart.order_status NOT NULL DEFAULT 'NEW'::gophermart.order_status, + uploaded_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); diff --git a/internal/app/usecase/auth/login.go b/internal/app/usecase/auth/login.go index 633956d..8e6f73f 100644 --- a/internal/app/usecase/auth/login.go +++ b/internal/app/usecase/auth/login.go @@ -7,26 +7,26 @@ import ( "github.com/Froctnow/yandex-go-diploma/internal/app/usecase/auth/errors" ) -func (u *authUseCase) Login(ctx context.Context, login string, password string) error { +func (u *authUseCase) Login(ctx context.Context, login string, password string) (string, error) { user, err := u.provider.GetUserForLogin(ctx, nil, login) if err != nil { err = fmt.Errorf("can't get user for login: %w", err) u.logger.ErrorCtx(ctx, err) - return err + return "", err } if user.ID == "" { err = errors.IncorrectLoginPasswordError{} u.logger.ErrorCtx(ctx, err) - return err + return "", err } if !u.checkPasswordHash(password, user.Password) { err = errors.IncorrectLoginPasswordError{} u.logger.ErrorCtx(ctx, err) - return err + return "", err } - return nil + return user.ID, nil } diff --git a/internal/app/usecase/auth/register.go b/internal/app/usecase/auth/register.go index 56a6ae1..5e620f7 100644 --- a/internal/app/usecase/auth/register.go +++ b/internal/app/usecase/auth/register.go @@ -16,7 +16,7 @@ func (u *authUseCase) Register(ctx context.Context, login string, password strin return err } - user, err := u.provider.CreateUser(ctx, nil, login, hashPassword) + userID, err := u.provider.CreateUser(ctx, nil, login, hashPassword) if err != nil { err = fmt.Errorf("can't create user: %w", err) @@ -24,7 +24,7 @@ func (u *authUseCase) Register(ctx context.Context, login string, password strin return err } - if user.ID == "" { + if userID == "" { err = errors.UserAlreadyExistsError{} u.logger.ErrorCtx(ctx, err) return err diff --git a/internal/app/usecase/auth/usecase.go b/internal/app/usecase/auth/usecase.go index 192da5a..b0a1969 100644 --- a/internal/app/usecase/auth/usecase.go +++ b/internal/app/usecase/auth/usecase.go @@ -26,7 +26,7 @@ func NewUseCase( type UseCase interface { Register(ctx context.Context, login string, password string) error - Login(ctx context.Context, login string, password string) error + Login(ctx context.Context, login string, password string) (string, error) } func (u *authUseCase) hashPassword(password string) (string, error) { diff --git a/internal/app/usecase/order/create_order.go b/internal/app/usecase/order/create_order.go new file mode 100644 index 0000000..78826c0 --- /dev/null +++ b/internal/app/usecase/order/create_order.go @@ -0,0 +1,52 @@ +package order + +import ( + "context" + "fmt" + + "github.com/Froctnow/yandex-go-diploma/internal/app/usecase/order/errors" +) + +func (u *orderUseCase) Create(ctx context.Context, orderNumber string, userID string) error { + err := u.checkUserOrder(ctx, orderNumber, userID) + + if err != nil { + u.logger.ErrorCtx(ctx, err) + return err + } + + insertedOrderNumber, err := u.provider.CreateOrder(ctx, nil, orderNumber, userID) + + if err != nil { + err = fmt.Errorf("failed to create order: %w", err) + u.logger.ErrorCtx(ctx, err) + return err + } + + if insertedOrderNumber == "" { + err = u.checkUserOrder(ctx, orderNumber, userID) + + if err != nil { + u.logger.ErrorCtx(ctx, err) + return err + } + + return errors.OrderAlreadyExists{} + } + + return nil +} + +func (u *orderUseCase) checkUserOrder(ctx context.Context, orderNumber string, userID string) error { + isUserOrderExists, err := u.provider.CheckUserOrder(ctx, nil, orderNumber, userID) + + if err != nil { + return fmt.Errorf("failed to check user order: %w", err) + } + + if isUserOrderExists { + return errors.UserOrderAlreadyExists{} + } + + return nil +} diff --git a/internal/app/usecase/order/errors/errors.go b/internal/app/usecase/order/errors/errors.go new file mode 100644 index 0000000..a22424b --- /dev/null +++ b/internal/app/usecase/order/errors/errors.go @@ -0,0 +1,15 @@ +package errors + +type UserOrderAlreadyExists struct { +} + +func (e UserOrderAlreadyExists) Error() string { + return "order of user already exists" +} + +type OrderAlreadyExists struct { +} + +func (e OrderAlreadyExists) Error() string { + return "order already exists" +} diff --git a/internal/app/usecase/order/usecase.go b/internal/app/usecase/order/usecase.go new file mode 100644 index 0000000..dfad44d --- /dev/null +++ b/internal/app/usecase/order/usecase.go @@ -0,0 +1,27 @@ +package order + +import ( + "context" + + "github.com/Froctnow/yandex-go-diploma/internal/app/gophermartprovider" + "github.com/Froctnow/yandex-go-diploma/pkg/logger" +) + +type orderUseCase struct { + logger logger.LogClient + provider gophermartprovider.GophermartProvider +} + +func NewUseCase( + logger logger.LogClient, + provider gophermartprovider.GophermartProvider, +) UseCase { + return &orderUseCase{ + logger: logger, + provider: provider, + } +} + +type UseCase interface { + Create(ctx context.Context, orderNumber string, userID string) error +} diff --git a/internal/app/validator/algorithm_luhn.go b/internal/app/validator/algorithm_luhn.go new file mode 100644 index 0000000..66087d2 --- /dev/null +++ b/internal/app/validator/algorithm_luhn.go @@ -0,0 +1,42 @@ +package validator + +import ( + "strings" + + "github.com/gobuffalo/validate" +) + +type AlgorithmLuhn struct { + Name string + Number int + Message string +} + +func (v *AlgorithmLuhn) IsValid(errors *validate.Errors) { + if v.Message == "" { + v.Message = "Number is not valid by Luhn algorithm" + } + + if (v.Number%10+checksum(v.Number/10))%10 != 0 { + errors.Add(strings.ToLower(v.Name), v.Message) + } +} + +func checksum(number int) int { + var luhn int + + for i := 0; number > 0; i++ { + cur := number % 10 + + if i%2 == 0 { // even + cur = cur * 2 + if cur > 9 { + cur = cur%10 + cur/10 + } + } + + luhn += cur + number = number / 10 + } + return luhn % 10 +} diff --git a/internal/app/validator/user_create_order.go b/internal/app/validator/user_create_order.go new file mode 100644 index 0000000..16fd81d --- /dev/null +++ b/internal/app/validator/user_create_order.go @@ -0,0 +1,28 @@ +package validator + +import ( + "strconv" + + "github.com/gobuffalo/validate" +) + +func (v *validator) UserCreateOrder(orderNumber string) *validate.Errors { + numericOrderNumber, err := strconv.Atoi(orderNumber) + + if err != nil { + errors := validate.NewErrors() + + errors.Add("OrderNumber", "Order number must be a number") + + return errors + } + + checks := []validate.Validator{ + &AlgorithmLuhn{ + Name: "OrderNumber", + Number: numericOrderNumber, + }, + } + errors := validate.Validate(checks...) + return errors +} diff --git a/internal/app/validator/validator.go b/internal/app/validator/validator.go index 0b86d38..24a3770 100644 --- a/internal/app/validator/validator.go +++ b/internal/app/validator/validator.go @@ -8,6 +8,7 @@ type validator struct{} type Validator interface { UserRegister(request *httpmodels.RegisterRequest) *validate.Errors UserLogin(request *httpmodels.LoginRequest) *validate.Errors + UserCreateOrder(orderNumber string) *validate.Errors } func New() Validator { diff --git a/internal/bootstrap/app.go b/internal/bootstrap/app.go index e13122a..355946a 100644 --- a/internal/bootstrap/app.go +++ b/internal/bootstrap/app.go @@ -13,6 +13,7 @@ import ( "github.com/Froctnow/yandex-go-diploma/internal/app/httpserver" "github.com/Froctnow/yandex-go-diploma/internal/app/migration" "github.com/Froctnow/yandex-go-diploma/internal/app/usecase/auth" + "github.com/Froctnow/yandex-go-diploma/internal/app/usecase/order" "github.com/Froctnow/yandex-go-diploma/internal/app/validator" "github.com/Froctnow/yandex-go-diploma/pkg/logger" ) @@ -38,6 +39,7 @@ func RunApp(ctx context.Context, cfg *config.Values, logger logger.LogClient) { gophermartProvider := gophermartprovider.NewGophermartProvider(gophermartDBconn) authUseCase := auth.NewUseCase(logger, gophermartProvider) + orderUseCase := order.NewUseCase(logger, gophermartProvider) validatorInstance := validator.New() _ = httpserver.NewGophermartServer( @@ -46,6 +48,7 @@ func RunApp(ctx context.Context, cfg *config.Values, logger logger.LogClient) { validatorInstance, cfg, authUseCase, + orderUseCase, ) exit := make(chan os.Signal, 1)