forked from ebhomengo/niki
Merge branch 'develop' into agent-structure-refactor
This commit is contained in:
commit
6819dde2ce
|
@ -0,0 +1,37 @@
|
|||
package adminhandler
|
||||
|
||||
import (
|
||||
adminserviceparam "git.gocasts.ir/ebhomengo/niki/param/admin/admin"
|
||||
"net/http"
|
||||
|
||||
httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// RefreshAccess godoc
|
||||
// @Summary Get a new access token by providing a refresh token
|
||||
// @Tags Admins
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Request body adminserviceparam.RefreshAccessRequest true "Refresh access request body"
|
||||
// @Success 200 {object} adminserviceparam.RefreshAccessResponse
|
||||
// @Failure 400 {string} "Bad Request"
|
||||
// @Failure 422 {string} "invalid or expired jwt"
|
||||
// @Failure 500 {string} "something went wrong"
|
||||
// @Router /admins/refresh-access [post].
|
||||
func (h Handler) RefreshAccess(c echo.Context) error {
|
||||
var req adminserviceparam.RefreshAccessRequest
|
||||
|
||||
if err := c.Bind(&req); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
resp, err := h.adminSvc.RefreshAccess(c.Request().Context(), req)
|
||||
if err != nil {
|
||||
msg, code := httpmsg.Error(err)
|
||||
|
||||
return echo.NewHTTPError(code, msg)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, resp)
|
||||
}
|
|
@ -13,6 +13,7 @@ func (h Handler) SetRoutes(e *echo.Echo) {
|
|||
//r.POST("/", h.Add).Name = "admin-addkindboxreq"
|
||||
r.POST("/register", h.Register, middleware.Auth(h.authSvc), middleware.AdminAuthorization(h.adminAuthorizeSvc, entity.AdminAdminRegisterPermission))
|
||||
r.POST("/login-by-phone", h.LoginByPhoneNumber)
|
||||
r.POST("/refresh-access", h.RefreshAccess)
|
||||
//nolint:gocritic
|
||||
//r.PATCH("/:id", h.Update).Name = "admin-updatekindboxreq"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package benefactorhandler
|
||||
|
||||
import (
|
||||
benefactorparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/benefactor"
|
||||
httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// RefreshAccess godoc
|
||||
// @Summary Get a new access token by providing your refresh token
|
||||
// @Tags Benefactors
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Request body benefactorparam.RefreshAccessRequest true "Refresh access token request body"
|
||||
// @Success 200 {object} benefactorparam.RefreshAccessResponse
|
||||
// @Failure 400 {string} "Bad Request"
|
||||
// @Failure 500 {string} "something went wrong"
|
||||
// @Router /benefactors/refresh-access [post].
|
||||
func (h Handler) RefreshAccess(c echo.Context) error {
|
||||
var req benefactorparam.RefreshAccessRequest
|
||||
|
||||
if err := c.Bind(&req); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
resp, err := h.benefactorSvc.RefreshAccess(c.Request().Context(), req)
|
||||
if err != nil {
|
||||
msg, code := httpmsg.Error(err)
|
||||
|
||||
return echo.NewHTTPError(code, msg)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, resp)
|
||||
}
|
|
@ -9,4 +9,5 @@ func (h Handler) SetRoutes(e *echo.Echo) {
|
|||
|
||||
r.POST("/send-otp", h.SendOtp)
|
||||
r.POST("/login-register", h.loginOrRegister)
|
||||
r.POST("/refresh-access", h.RefreshAccess)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ func Auth(service authservice.Service) echo.MiddlewareFunc {
|
|||
// TODO - as sign method string to config
|
||||
SigningMethod: "HS256",
|
||||
ParseTokenFunc: func(c echo.Context, auth string) (interface{}, error) {
|
||||
claims, err := service.ParseToken(auth)
|
||||
claims, err := service.ParseBearerToken(auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
134
docs/docs.go
134
docs/docs.go
|
@ -1013,6 +1013,57 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/admins/refresh-access": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Admins"
|
||||
],
|
||||
"summary": "Get a new access token by providing a refresh token",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Refresh access request body",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/adminserviceparam.RefreshAccessRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/adminserviceparam.RefreshAccessResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "invalid or expired jwt",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "something went wrong",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admins/register": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
@ -2487,6 +2538,51 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/benefactors/refresh-access": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Benefactors"
|
||||
],
|
||||
"summary": "Get a new access token by providing your refresh token",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Refresh access token request body",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/benefactoreparam.RefreshAccessRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/benefactoreparam.RefreshAccessResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "something went wrong",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/benefactors/send-otp": {
|
||||
"post": {
|
||||
"description": "This endpoint sends an OTP to the benefactor's phone number for verification purposes.",
|
||||
|
@ -2960,7 +3056,11 @@ const docTemplate = `{
|
|||
},
|
||||
"deliver_refer_date": {
|
||||
"type": "string",
|
||||
"example": "2025-01-02 15:04:05"
|
||||
"example": "2025-01-02T15:04:05Z"
|
||||
},
|
||||
"deliver_refer_time_id": {
|
||||
"type": "integer",
|
||||
"example": 1
|
||||
},
|
||||
"kind_box_type": {
|
||||
"allOf": [
|
||||
|
@ -3153,6 +3253,22 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"adminserviceparam.RefreshAccessRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refresh_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"adminserviceparam.RefreshAccessResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"adminserviceparam.RegisterRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -3486,6 +3602,22 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"benefactoreparam.RefreshAccessRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refresh_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"benefactoreparam.RefreshAccessResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"benefactoreparam.SendOtpRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -1002,6 +1002,57 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/admins/refresh-access": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Admins"
|
||||
],
|
||||
"summary": "Get a new access token by providing a refresh token",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Refresh access request body",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/adminserviceparam.RefreshAccessRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/adminserviceparam.RefreshAccessResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "invalid or expired jwt",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "something went wrong",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admins/register": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
@ -2476,6 +2527,51 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/benefactors/refresh-access": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Benefactors"
|
||||
],
|
||||
"summary": "Get a new access token by providing your refresh token",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Refresh access token request body",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/benefactoreparam.RefreshAccessRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/benefactoreparam.RefreshAccessResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "something went wrong",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/benefactors/send-otp": {
|
||||
"post": {
|
||||
"description": "This endpoint sends an OTP to the benefactor's phone number for verification purposes.",
|
||||
|
@ -2949,7 +3045,11 @@
|
|||
},
|
||||
"deliver_refer_date": {
|
||||
"type": "string",
|
||||
"example": "2025-01-02 15:04:05"
|
||||
"example": "2025-01-02T15:04:05Z"
|
||||
},
|
||||
"deliver_refer_time_id": {
|
||||
"type": "integer",
|
||||
"example": 1
|
||||
},
|
||||
"kind_box_type": {
|
||||
"allOf": [
|
||||
|
@ -3142,6 +3242,22 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"adminserviceparam.RefreshAccessRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refresh_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"adminserviceparam.RefreshAccessResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"adminserviceparam.RegisterRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -3475,6 +3591,22 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"benefactoreparam.RefreshAccessRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refresh_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"benefactoreparam.RefreshAccessResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"benefactoreparam.SendOtpRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -287,8 +287,11 @@ definitions:
|
|||
example: 1
|
||||
type: integer
|
||||
deliver_refer_date:
|
||||
example: "2025-01-02 15:04:05"
|
||||
example: "2025-01-02T15:04:05Z"
|
||||
type: string
|
||||
deliver_refer_time_id:
|
||||
example: 1
|
||||
type: integer
|
||||
kind_box_type:
|
||||
allOf:
|
||||
- $ref: '#/definitions/entity.KindBoxType'
|
||||
|
@ -412,6 +415,16 @@ definitions:
|
|||
tokens:
|
||||
$ref: '#/definitions/adminserviceparam.Tokens'
|
||||
type: object
|
||||
adminserviceparam.RefreshAccessRequest:
|
||||
properties:
|
||||
refresh_token:
|
||||
type: string
|
||||
type: object
|
||||
adminserviceparam.RefreshAccessResponse:
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
type: object
|
||||
adminserviceparam.RegisterRequest:
|
||||
properties:
|
||||
description:
|
||||
|
@ -628,6 +641,16 @@ definitions:
|
|||
tokens:
|
||||
$ref: '#/definitions/benefactoreparam.Tokens'
|
||||
type: object
|
||||
benefactoreparam.RefreshAccessRequest:
|
||||
properties:
|
||||
refresh_token:
|
||||
type: string
|
||||
type: object
|
||||
benefactoreparam.RefreshAccessResponse:
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
type: object
|
||||
benefactoreparam.SendOtpRequest:
|
||||
properties:
|
||||
phone_number:
|
||||
|
@ -1598,6 +1621,39 @@ paths:
|
|||
summary: "Admin login by\tPhoneNumber"
|
||||
tags:
|
||||
- Admins
|
||||
/admins/refresh-access:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: Refresh access request body
|
||||
in: body
|
||||
name: Request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/adminserviceparam.RefreshAccessRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/adminserviceparam.RefreshAccessResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
type: string
|
||||
"422":
|
||||
description: invalid or expired jwt
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: something went wrong
|
||||
schema:
|
||||
type: string
|
||||
summary: Get a new access token by providing a refresh token
|
||||
tags:
|
||||
- Admins
|
||||
/admins/register:
|
||||
post:
|
||||
consumes:
|
||||
|
@ -2571,6 +2627,35 @@ paths:
|
|||
summary: Login or register a benefactor
|
||||
tags:
|
||||
- Benefactors
|
||||
/benefactors/refresh-access:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: Refresh access token request body
|
||||
in: body
|
||||
name: Request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/benefactoreparam.RefreshAccessRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/benefactoreparam.RefreshAccessResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
type: string
|
||||
"500":
|
||||
description: something went wrong
|
||||
schema:
|
||||
type: string
|
||||
summary: Get a new access token by providing your refresh token
|
||||
tags:
|
||||
- Benefactors
|
||||
/benefactors/send-otp:
|
||||
post:
|
||||
consumes:
|
||||
|
|
|
@ -12,4 +12,5 @@ type Benefactor struct {
|
|||
Gender Gender
|
||||
BirthDate time.Time
|
||||
Role UserRole
|
||||
Status BenefactorStatus
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package entity
|
||||
|
||||
type BenefactorStatus string
|
||||
|
||||
const (
|
||||
BenefactorActiveStatus = BenefactorStatus("active")
|
||||
BenefactorInactiveStatus = BenefactorStatus("inactive")
|
||||
)
|
||||
|
||||
var BenefactorStatusStrings = map[BenefactorStatus]string{
|
||||
BenefactorActiveStatus: "active",
|
||||
BenefactorInactiveStatus: "inactive",
|
||||
}
|
||||
|
||||
func (b BenefactorStatus) IsValid() bool {
|
||||
_, ok := BenefactorStatusStrings[b]
|
||||
|
||||
return ok
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package adminserviceparam
|
||||
|
||||
type RefreshAccessRequest struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
type RefreshAccessResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
|
@ -1,12 +1,16 @@
|
|||
package adminkindboxreqparam
|
||||
|
||||
import entity "git.gocasts.ir/ebhomengo/niki/entity"
|
||||
import (
|
||||
entity "git.gocasts.ir/ebhomengo/niki/entity"
|
||||
"time"
|
||||
)
|
||||
|
||||
type KindBoxReqAddRequest struct {
|
||||
BenefactorID uint `json:"benefactor_id" example:"1"`
|
||||
KindBoxType entity.KindBoxType `json:"kind_box_type" example:"on-table"`
|
||||
DeliverAddressID uint `json:"deliver_address_id" example:"1"`
|
||||
DeliverReferDate string `json:"deliver_refer_date" example:"2025-01-02 15:04:05"`
|
||||
DeliverReferDate time.Time `json:"deliver_refer_date" example:"2025-01-02T15:04:05Z"`
|
||||
DeliverReferTimeID uint `json:"deliver_refer_time_id" example:"1"`
|
||||
CountRequested uint `json:"count_requested" example:"2"`
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package benefactoreparam
|
||||
|
||||
type RefreshAccessRequest struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
type RefreshAccessResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
|
@ -37,4 +37,6 @@ const (
|
|||
ErrorMsgAssignReceiverAgentKindBoxStatus = "only ready to return kindboxes can be assigned to a receiver agent"
|
||||
ErrorMsgReturnKindBoxStatus = "only returned kindboxes can be enumerated"
|
||||
ErrorMsgInvalidSerialNumberRange = "invalid serial number range"
|
||||
ErrorMsgInvalidOrExpiredJwt = "invalid or expired jwt"
|
||||
ErrorMsgInvalidRefreshToken = "invalid refresh token"
|
||||
)
|
||||
|
|
|
@ -83,14 +83,11 @@ func (d *DB) GetAdminByID(ctx context.Context, adminID uint) (entity.Admin, erro
|
|||
row := stmt.QueryRowContext(ctx, adminID)
|
||||
admin, err := scanAdmin(row)
|
||||
if err != nil {
|
||||
sErr := sql.ErrNoRows
|
||||
//TODO-errorsas: second argument to errors.As should not be *error
|
||||
//nolint
|
||||
if errors.As(err, &sErr) {
|
||||
return entity.Admin{}, nil
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return entity.Admin{}, richerror.New(op).WithKind(richerror.KindNotFound).
|
||||
WithMessage(errmsg.ErrorMsgNotFound)
|
||||
}
|
||||
|
||||
// TODO - log unexpected error for better observability
|
||||
return entity.Admin{}, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
|
|
@ -26,15 +26,11 @@ func (d *DB) GetAdminByPhoneNumber(ctx context.Context, phoneNumber string) (ent
|
|||
row := stmt.QueryRowContext(ctx, phoneNumber)
|
||||
admin, err := scanAdmin(row)
|
||||
if err != nil {
|
||||
sErr := sql.ErrNoRows
|
||||
//TODO-errorsas: second argument to errors.As should not be *error
|
||||
//nolint
|
||||
if errors.As(err, &sErr) {
|
||||
return entity.Admin{}, richerror.New(op).WithErr(sErr).
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return entity.Admin{}, richerror.New(op).
|
||||
WithMessage(errmsg.ErrorMsgNotFound).WithKind(richerror.KindNotFound)
|
||||
}
|
||||
|
||||
// TODO - log unexpected error for better observability
|
||||
return entity.Admin{}, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
|
|
@ -2,142 +2,39 @@ package mysqlbenefactor
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
)
|
||||
|
||||
func (d *DB) IsExistBenefactorByPhoneNumber(ctx context.Context, phoneNumber string) (bool, entity.Benefactor, error) {
|
||||
const op = "mysqlbenefactor.IsExistBenefactorByPhoneNumber"
|
||||
|
||||
query := `select * from benefactors where phone_number = ?`
|
||||
//nolint
|
||||
stmt, err := d.conn.PrepareStatement(ctx, mysql.StatementKeyBenefactorIsExistByPhoneNumber, query)
|
||||
bnf, err := d.GetByPhoneNumber(ctx, phoneNumber)
|
||||
if err != nil {
|
||||
return false, entity.Benefactor{}, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantPrepareStatement).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
row := stmt.QueryRowContext(ctx, phoneNumber)
|
||||
Benefactor, err := scanBenefactor(row)
|
||||
if err != nil {
|
||||
sErr := sql.ErrNoRows
|
||||
//TODO-errorsas: second argument to errors.As should not be *error
|
||||
//nolint
|
||||
if errors.As(err, &sErr) {
|
||||
var richErr richerror.RichError
|
||||
if errors.As(err, &richErr) && richErr.Kind() == richerror.KindNotFound {
|
||||
return false, entity.Benefactor{}, nil
|
||||
}
|
||||
|
||||
// TODO - log unexpected error for better observability
|
||||
return false, entity.Benefactor{}, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
|
||||
return false, entity.Benefactor{}, richerror.New(op).WithErr(err)
|
||||
}
|
||||
|
||||
return true, Benefactor, nil
|
||||
return true, bnf, nil
|
||||
}
|
||||
|
||||
func (d *DB) IsExistBenefactorByID(ctx context.Context, id uint) (bool, error) {
|
||||
const op = "mysqlbenefactor.IsExistBenefactorByID"
|
||||
|
||||
query := `select * from benefactors where id = ?`
|
||||
//nolint
|
||||
stmt, err := d.conn.PrepareStatement(ctx, mysql.StatementKeyBenefactorIsExistByID, query)
|
||||
_, err := d.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return false, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantPrepareStatement).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
row := stmt.QueryRowContext(ctx, id)
|
||||
_, err = scanBenefactor(row)
|
||||
if err != nil {
|
||||
sErr := sql.ErrNoRows
|
||||
//TODO-errorsas: second argument to errors.As should not be *error
|
||||
//nolint
|
||||
if errors.As(err, &sErr) {
|
||||
var richErr richerror.RichError
|
||||
if errors.As(err, &richErr) && richErr.Kind() == richerror.KindNotFound {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// TODO - log unexpected error for better observability
|
||||
return false, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
|
||||
return false, richerror.New(op).WithErr(err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func scanBenefactor(scanner mysql.Scanner) (entity.Benefactor, error) {
|
||||
var createdAt, updatedAt time.Time
|
||||
var benefactor entity.Benefactor
|
||||
// TODO - use db model and mapper between entity and db model OR use this approach
|
||||
|
||||
var benefactorNullableFields nullableFields
|
||||
|
||||
err := scanner.Scan(&benefactor.ID, &benefactorNullableFields.firstName,
|
||||
&benefactorNullableFields.lastName, &benefactor.PhoneNumber, &benefactorNullableFields.description,
|
||||
&benefactorNullableFields.email, &benefactorNullableFields.genderStr,
|
||||
&benefactorNullableFields.birthdate, &createdAt, &updatedAt)
|
||||
|
||||
mapNotNullToBenefactor(benefactorNullableFields, &benefactor)
|
||||
|
||||
return benefactor, err
|
||||
}
|
||||
|
||||
type nullableFields struct {
|
||||
firstName sql.NullString
|
||||
lastName sql.NullString
|
||||
description sql.NullString
|
||||
email sql.NullString
|
||||
genderStr sql.NullString
|
||||
birthdate sql.NullTime
|
||||
}
|
||||
|
||||
// TODO - find the other solution.
|
||||
func mapNotNullToBenefactor(data nullableFields, benefactor *entity.Benefactor) {
|
||||
if data.firstName.Valid {
|
||||
benefactor.FirstName = data.firstName.String
|
||||
}
|
||||
if data.lastName.Valid {
|
||||
benefactor.LastName = data.lastName.String
|
||||
}
|
||||
|
||||
if data.description.Valid {
|
||||
benefactor.Description = data.description.String
|
||||
}
|
||||
if data.email.Valid {
|
||||
benefactor.Email = data.email.String
|
||||
}
|
||||
|
||||
if data.genderStr.Valid {
|
||||
benefactor.Gender = entity.Gender(data.genderStr.String)
|
||||
}
|
||||
if data.birthdate.Valid {
|
||||
benefactor.BirthDate = data.birthdate.Time
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DB) GetByID(ctx context.Context, benefactorID uint) (entity.Benefactor, error) {
|
||||
const op = "mysqlbenefactor.IsExistBenefactorByID"
|
||||
|
||||
row := d.conn.Conn().QueryRowContext(ctx, `select * from benefactors where id = ?`, benefactorID)
|
||||
|
||||
bnf, err := scanBenefactor(row)
|
||||
if err != nil {
|
||||
sErr := sql.ErrNoRows
|
||||
//TODO-errorsas: second argument to errors.As should not be *error
|
||||
//nolint
|
||||
if errors.As(err, &sErr) {
|
||||
return bnf, nil
|
||||
}
|
||||
|
||||
// TODO - log unexpected error for better observability
|
||||
return bnf, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
return bnf, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package mysqlbenefactor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
)
|
||||
|
||||
func (d *DB) GetByID(ctx context.Context, id uint) (entity.Benefactor, error) {
|
||||
const op = "mysqlbenefactor.GetByID"
|
||||
|
||||
query := `select * from benefactors where id = ?`
|
||||
//nolint
|
||||
stmt, err := d.conn.PrepareStatement(ctx, mysql.StatementKeyBenefactorGetByID, query)
|
||||
if err != nil {
|
||||
return entity.Benefactor{}, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantPrepareStatement).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
row := stmt.QueryRowContext(ctx, id)
|
||||
bnf, err := scanBenefactor(row)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return entity.Benefactor{}, richerror.New(op).WithKind(richerror.KindNotFound).
|
||||
WithMessage(errmsg.ErrorMsgNotFound)
|
||||
}
|
||||
|
||||
return entity.Benefactor{}, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
return bnf, nil
|
||||
}
|
||||
|
||||
func (d *DB) GetByPhoneNumber(ctx context.Context, phoneNumber string) (entity.Benefactor, error) {
|
||||
const op = "mysqlbenefactor.GetByPhoneNumber"
|
||||
|
||||
query := `select * from benefactors where phone_number = ?`
|
||||
//nolint
|
||||
stmt, err := d.conn.PrepareStatement(ctx, mysql.StatementKeyBenefactorGetByPhoneNumber, query)
|
||||
if err != nil {
|
||||
return entity.Benefactor{}, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantPrepareStatement).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
row := stmt.QueryRowContext(ctx, phoneNumber)
|
||||
bnf, err := scanBenefactor(row)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return entity.Benefactor{}, richerror.New(op).WithKind(richerror.KindNotFound).
|
||||
WithMessage(errmsg.ErrorMsgNotFound)
|
||||
}
|
||||
|
||||
return entity.Benefactor{}, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
return bnf, nil
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package mysqlbenefactor
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
|
||||
"time"
|
||||
)
|
||||
|
||||
func scanBenefactor(scanner mysql.Scanner) (entity.Benefactor, error) {
|
||||
var createdAt, updatedAt time.Time
|
||||
var benefactor entity.Benefactor
|
||||
// TODO - use db model and mapper between entity and db model OR use this approach
|
||||
|
||||
var benefactorNullableFields nullableFields
|
||||
|
||||
err := scanner.Scan(&benefactor.ID, &benefactorNullableFields.firstName,
|
||||
&benefactorNullableFields.lastName, &benefactor.PhoneNumber, &benefactorNullableFields.description,
|
||||
&benefactorNullableFields.email, &benefactorNullableFields.genderStr,
|
||||
&benefactorNullableFields.birthdate, &createdAt, &updatedAt, &benefactor.Status)
|
||||
|
||||
mapNotNullToBenefactor(benefactorNullableFields, &benefactor)
|
||||
|
||||
return benefactor, err
|
||||
}
|
||||
|
||||
type nullableFields struct {
|
||||
firstName sql.NullString
|
||||
lastName sql.NullString
|
||||
description sql.NullString
|
||||
email sql.NullString
|
||||
genderStr sql.NullString
|
||||
birthdate sql.NullTime
|
||||
}
|
||||
|
||||
// TODO - find the other solution.
|
||||
func mapNotNullToBenefactor(data nullableFields, benefactor *entity.Benefactor) {
|
||||
if data.firstName.Valid {
|
||||
benefactor.FirstName = data.firstName.String
|
||||
}
|
||||
if data.lastName.Valid {
|
||||
benefactor.LastName = data.lastName.String
|
||||
}
|
||||
|
||||
if data.description.Valid {
|
||||
benefactor.Description = data.description.String
|
||||
}
|
||||
if data.email.Valid {
|
||||
benefactor.Email = data.email.String
|
||||
}
|
||||
|
||||
if data.genderStr.Valid {
|
||||
benefactor.Gender = entity.Gender(data.genderStr.String)
|
||||
}
|
||||
if data.birthdate.Valid {
|
||||
benefactor.BirthDate = data.birthdate.Time
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ package mysqlkindboxreq
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
|
@ -26,7 +25,7 @@ func (d *DB) AddKindBoxReq(ctx context.Context, kindBoxReq entity.KindBoxReq) (e
|
|||
kindBoxReq.DeliverReferTimeID, kindBoxReq.Status)
|
||||
if err != nil {
|
||||
return entity.KindBoxReq{}, richerror.New(op).WithErr(err).
|
||||
WithMessage(errmsg.ErrorMsgNotFound).WithKind(richerror.KindUnexpected)
|
||||
WithKind(richerror.KindUnexpected)
|
||||
}
|
||||
|
||||
//nolint
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
-- +migrate Up
|
||||
ALTER TABLE `benefactors` ADD COLUMN `status` ENUM('active','inactive') NOT NULL DEFAULT 'active';
|
||||
|
||||
-- +migrate Down
|
||||
ALTER TABLE `benefactors` DROP COLUMN `status`;
|
|
@ -22,8 +22,8 @@ const (
|
|||
StatementKeyAdminGetByID
|
||||
StatementKeyAdminGetByPhoneNumber
|
||||
StatementKeyAdminAgentGetAll
|
||||
StatementKeyBenefactorIsExistByID
|
||||
StatementKeyBenefactorIsExistByPhoneNumber
|
||||
StatementKeyBenefactorGetByID
|
||||
StatementKeyBenefactorGetByPhoneNumber
|
||||
StatementKeyBenefactorCreate
|
||||
StatementKeyKindBoxAdd
|
||||
StatementKeyKindBoxAssignReceiverAgent
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package adminservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
adminserviceparam "git.gocasts.ir/ebhomengo/niki/param/admin/admin"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
)
|
||||
|
||||
func (s Service) RefreshAccess(ctx context.Context, req adminserviceparam.RefreshAccessRequest) (adminserviceparam.RefreshAccessResponse, error) {
|
||||
const op = "adminservice.RefreshAccess"
|
||||
claims, err := s.auth.ParseRefreshToken(req.RefreshToken)
|
||||
if err != nil {
|
||||
return adminserviceparam.RefreshAccessResponse{}, richerror.New(op).WithKind(richerror.KindInvalid).WithMessage(errmsg.ErrorMsgInvalidOrExpiredJwt)
|
||||
}
|
||||
|
||||
admin, err := s.repo.GetAdminByID(ctx, claims.UserID)
|
||||
if err != nil {
|
||||
return adminserviceparam.RefreshAccessResponse{}, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
|
||||
}
|
||||
if admin.Status == entity.AdminInactiveStatus {
|
||||
return adminserviceparam.RefreshAccessResponse{}, richerror.New(op).WithKind(richerror.KindForbidden).WithMessage(errmsg.ErrorMsgAdminNotAllowed)
|
||||
}
|
||||
|
||||
accessToken, err := s.auth.CreateAccessToken(entity.Authenticable{
|
||||
ID: claims.UserID,
|
||||
Role: claims.Role,
|
||||
})
|
||||
if err != nil {
|
||||
return adminserviceparam.RefreshAccessResponse{}, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
|
||||
}
|
||||
|
||||
return adminserviceparam.RefreshAccessResponse{AccessToken: accessToken}, nil
|
||||
}
|
|
@ -3,6 +3,7 @@ package adminservice
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.gocasts.ir/ebhomengo/niki/service/auth"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/config"
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
|
@ -10,9 +11,10 @@ import (
|
|||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type AuthGenerator interface {
|
||||
type AuthService interface {
|
||||
CreateAccessToken(admin entity.Authenticable) (string, error)
|
||||
CreateRefreshToken(admin entity.Authenticable) (string, error)
|
||||
ParseRefreshToken(refreshToken string) (*auth.Claims, error)
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
|
@ -23,11 +25,11 @@ type Repository interface {
|
|||
|
||||
type Service struct {
|
||||
repo Repository
|
||||
auth AuthGenerator
|
||||
auth AuthService
|
||||
vld validator.Validator
|
||||
}
|
||||
|
||||
func New(repo Repository, auth AuthGenerator, vld validator.Validator) Service {
|
||||
func New(repo Repository, auth AuthService, vld validator.Validator) Service {
|
||||
return Service{
|
||||
repo: repo,
|
||||
auth: auth,
|
||||
|
|
|
@ -2,8 +2,6 @@ package adminkindboxreqservice
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
param "git.gocasts.ir/ebhomengo/niki/param/admin/kind_box_req"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
|
@ -14,15 +12,12 @@ func (s Service) Add(ctx context.Context, req param.KindBoxReqAddRequest) (param
|
|||
if fieldErrors, vErr := s.vld.ValidateAddRequest(ctx, req); vErr != nil {
|
||||
return param.KindBoxReqAddResponse{FieldErrors: fieldErrors}, richerror.New(op).WithErr(vErr)
|
||||
}
|
||||
date, tErr := time.Parse(time.DateTime, req.DeliverReferDate)
|
||||
if tErr != nil {
|
||||
return param.KindBoxReqAddResponse{}, richerror.New(op).WithErr(tErr).WithKind(richerror.KindInvalid)
|
||||
}
|
||||
kindBoxReq, err := s.repo.AddKindBoxReq(ctx, entity.KindBoxReq{
|
||||
BenefactorID: req.BenefactorID,
|
||||
KindBoxType: req.KindBoxType,
|
||||
DeliverAddressID: req.DeliverAddressID,
|
||||
DeliverReferDate: date,
|
||||
DeliverReferDate: req.DeliverReferDate,
|
||||
DeliverReferTimeID: req.DeliverReferTimeID,
|
||||
CountRequested: req.CountRequested,
|
||||
Status: entity.KindBoxReqPendingStatus,
|
||||
})
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (s Service) CreateAccessToken(user entity.Authenticable) (string, error) {
|
||||
return s.createToken(user.ID, user.Role, s.Config.AccessSubject, s.Config.AccessExpirationTime)
|
||||
}
|
||||
|
||||
func (s Service) CreateRefreshToken(user entity.Authenticable) (string, error) {
|
||||
return s.createToken(user.ID, user.Role, s.Config.RefreshSubject, s.Config.RefreshExpirationTime)
|
||||
}
|
||||
|
||||
func (s Service) createToken(userID uint, role, subject string, expireDuration time.Duration) (string, error) {
|
||||
// create a signer for rsa 256
|
||||
// TODO - replace with rsa 256 RS256 - https://github.com/golang-jwt/jwt/blob/main/http_example_test.go
|
||||
|
||||
// set our claims
|
||||
claims := Claims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: subject,
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(expireDuration)),
|
||||
},
|
||||
UserID: userID,
|
||||
Role: role,
|
||||
}
|
||||
|
||||
// TODO - add sign method to config
|
||||
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := accessToken.SignedString([]byte(s.Config.SignKey))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (s Service) ParseBearerToken(bearerToken string) (*Claims, error) {
|
||||
tokenStr := strings.Replace(bearerToken, "Bearer ", "", 1)
|
||||
|
||||
return s.parseToken(tokenStr)
|
||||
}
|
||||
|
||||
func (s Service) ParseRefreshToken(refreshToken string) (*Claims, error) {
|
||||
claims, err := s.parseToken(refreshToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if claims.Subject != s.Config.RefreshSubject {
|
||||
return nil, fmt.Errorf(errmsg.ErrorMsgInvalidRefreshToken)
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (s Service) parseToken(token string) (*Claims, error) {
|
||||
// https://pkg.go.dev/github.com/golang-jwt/jwt/v5#example-ParseWithClaims-CustomClaimsType
|
||||
t, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(s.Config.SignKey), nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if claims, ok := t.Claims.(*Claims); ok && t.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -25,54 +21,3 @@ func New(cfg Config) Service {
|
|||
Config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (s Service) CreateAccessToken(user entity.Authenticable) (string, error) {
|
||||
return s.createToken(user.ID, user.Role, s.Config.AccessSubject, s.Config.AccessExpirationTime)
|
||||
}
|
||||
|
||||
func (s Service) CreateRefreshToken(user entity.Authenticable) (string, error) {
|
||||
return s.createToken(user.ID, user.Role, s.Config.RefreshSubject, s.Config.RefreshExpirationTime)
|
||||
}
|
||||
|
||||
func (s Service) ParseToken(bearerToken string) (*Claims, error) {
|
||||
// https://pkg.go.dev/github.com/golang-jwt/jwt/v5#example-ParseWithClaims-CustomClaimsType
|
||||
|
||||
tokenStr := strings.Replace(bearerToken, "Bearer ", "", 1)
|
||||
|
||||
token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(s.Config.SignKey), nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s Service) createToken(userID uint, role, subject string, expireDuration time.Duration) (string, error) {
|
||||
// create a signer for rsa 256
|
||||
// TODO - replace with rsa 256 RS256 - https://github.com/golang-jwt/jwt/blob/main/http_example_test.go
|
||||
|
||||
// set our claims
|
||||
claims := Claims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: subject,
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(expireDuration)),
|
||||
},
|
||||
UserID: userID,
|
||||
Role: role,
|
||||
}
|
||||
|
||||
// TODO - add sign method to config
|
||||
accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := accessToken.SignedString([]byte(s.Config.SignKey))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package benefactorservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
benefactorparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/benefactor"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
)
|
||||
|
||||
func (s Service) RefreshAccess(ctx context.Context, req benefactorparam.RefreshAccessRequest) (benefactorparam.RefreshAccessResponse, error) {
|
||||
const op = "adminservice.RefreshAccess"
|
||||
claims, err := s.auth.ParseRefreshToken(req.RefreshToken)
|
||||
if err != nil {
|
||||
return benefactorparam.RefreshAccessResponse{}, richerror.New(op).WithKind(richerror.KindInvalid).WithMessage(errmsg.ErrorMsgInvalidOrExpiredJwt)
|
||||
}
|
||||
|
||||
benefactor, err := s.repo.GetByID(ctx, claims.UserID)
|
||||
if err != nil {
|
||||
return benefactorparam.RefreshAccessResponse{}, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
|
||||
}
|
||||
if benefactor.Status == entity.BenefactorInactiveStatus {
|
||||
return benefactorparam.RefreshAccessResponse{}, richerror.New(op).WithKind(richerror.KindForbidden).WithMessage(errmsg.ErrorMsgUserNotAllowed)
|
||||
}
|
||||
|
||||
accessToken, err := s.auth.CreateAccessToken(entity.Authenticable{
|
||||
ID: claims.UserID,
|
||||
Role: claims.Role,
|
||||
})
|
||||
if err != nil {
|
||||
return benefactorparam.RefreshAccessResponse{}, richerror.New(op).WithKind(richerror.KindUnexpected).WithErr(err)
|
||||
}
|
||||
|
||||
return benefactorparam.RefreshAccessResponse{AccessToken: accessToken}, nil
|
||||
}
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
func (s Service) SendOtp(ctx context.Context, req benefactoreparam.SendOtpRequest) (benefactoreparam.SendOtpResponse, error) {
|
||||
const op = "benefactorservice.SendOtp"
|
||||
if fieldErrors, vErr := s.vld.ValidateSendOtpRequest(req); vErr != nil {
|
||||
if fieldErrors, vErr := s.vld.ValidateSendOtpRequest(ctx, req); vErr != nil {
|
||||
return benefactoreparam.SendOtpResponse{FieldErrors: fieldErrors}, richerror.New(op).WithErr(vErr)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package benefactorservice
|
|||
|
||||
import (
|
||||
"context"
|
||||
"git.gocasts.ir/ebhomengo/niki/service/auth"
|
||||
"time"
|
||||
|
||||
smscontract "git.gocasts.ir/ebhomengo/niki/contract/sms"
|
||||
|
@ -22,9 +23,10 @@ type Repository interface {
|
|||
GetByID(ctx context.Context, benefactorID uint) (entity.Benefactor, error)
|
||||
}
|
||||
|
||||
type AuthGenerator interface {
|
||||
CreateAccessToken(benefactor entity.Authenticable) (string, error)
|
||||
CreateRefreshToken(benefactor entity.Authenticable) (string, error)
|
||||
type AuthService interface {
|
||||
CreateAccessToken(admin entity.Authenticable) (string, error)
|
||||
CreateRefreshToken(admin entity.Authenticable) (string, error)
|
||||
ParseRefreshToken(refreshToken string) (*auth.Claims, error)
|
||||
}
|
||||
|
||||
type RedisOtp interface {
|
||||
|
@ -38,13 +40,13 @@ type Service struct {
|
|||
config Config
|
||||
redisOtp RedisOtp
|
||||
smsAdapter smscontract.SmsAdapter
|
||||
auth AuthGenerator
|
||||
auth AuthService
|
||||
repo Repository
|
||||
vld benefactorvalidator.Validator
|
||||
}
|
||||
|
||||
func New(cfg Config, redisOtp RedisOtp, smsAdapter smscontract.SmsAdapter,
|
||||
auth AuthGenerator, repo Repository, vld benefactorvalidator.Validator,
|
||||
auth AuthService, repo Repository, vld benefactorvalidator.Validator,
|
||||
) Service {
|
||||
return Service{
|
||||
config: cfg,
|
||||
|
|
|
@ -94,7 +94,7 @@ func New(cfg config.Config, db *mysql.DB, rds *redis.Adapter, smsAdapter smscont
|
|||
BenefactorAuthSvc = auth.New(cfg.BenefactorAuth)
|
||||
BenefactorReferTimeSvc = benefactorrefertimeservice.New(referTimeRepo)
|
||||
|
||||
BenefactorVld = benefactorvalidator.New()
|
||||
BenefactorVld = benefactorvalidator.New(benefactorRepo)
|
||||
BenefactorSvc = benefactorservice.New(cfg.BenefactorSvc, redisOtp, smsAdapter, BenefactorAuthSvc, benefactorRepo, BenefactorVld)
|
||||
BenefactorAddressVld = benefactoraddressvalidator.New(BenefactorSvc, addressRepo)
|
||||
BenefactorAddressSvc = benefactoraddressservice.New(addressRepo, BenefactorAddressVld)
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
func (v Validator) ValidateLoginWithPhoneNumberRequest(ctx context.Context, req adminserviceparam.LoginWithPhoneNumberRequest) (map[string]string, error) {
|
||||
const op = "adminvalidator.ValidateRegisterRequest"
|
||||
const op = "adminvalidator.ValidateLoginWithPhoneNumberRequest"
|
||||
|
||||
if err := validation.ValidateStruct(&req,
|
||||
// TODO - add regex
|
||||
|
@ -22,7 +22,9 @@ func (v Validator) ValidateLoginWithPhoneNumberRequest(ctx context.Context, req
|
|||
validation.Field(&req.PhoneNumber,
|
||||
validation.Required,
|
||||
validation.Match(regexp.MustCompile(phoneNumberRegex)).Error(errmsg.ErrorMsgPhoneNumberIsNotValid),
|
||||
validation.By(v.doesAdminExistByPhoneNumber(ctx)))); err != nil {
|
||||
validation.By(v.doesAdminExistByPhoneNumber(ctx)),
|
||||
validation.By(v.isAdminAllowed(ctx)),
|
||||
)); err != nil {
|
||||
fieldErrors := make(map[string]string)
|
||||
|
||||
vErr := validation.Errors{}
|
||||
|
|
|
@ -3,6 +3,7 @@ package adminvalidator
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
"testing"
|
||||
|
||||
adminserviceparam "git.gocasts.ir/ebhomengo/niki/param/admin/admin"
|
||||
|
@ -23,7 +24,10 @@ func TestValidateLoginWithPhoneNumberRequest(t *testing.T) {
|
|||
Password: validPassword,
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, validPhoneNumber).Return(true, nil).Once()
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, req.PhoneNumber).Return(true, nil).Once()
|
||||
mockRepo.EXPECT().GetAdminByPhoneNumber(ctx, req.PhoneNumber).Return(entity.Admin{
|
||||
Status: entity.AdminActiveStatus,
|
||||
}, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
|
@ -48,7 +52,10 @@ func TestValidateLoginWithPhoneNumberRequest(t *testing.T) {
|
|||
Password: "",
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, validPhoneNumber).Return(true, nil).Once()
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, req.PhoneNumber).Return(true, nil).Once()
|
||||
mockRepo.EXPECT().GetAdminByPhoneNumber(ctx, req.PhoneNumber).Return(entity.Admin{
|
||||
Status: entity.AdminActiveStatus,
|
||||
}, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
|
@ -74,7 +81,10 @@ func TestValidateLoginWithPhoneNumberRequest(t *testing.T) {
|
|||
Password: "short",
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, validPhoneNumber).Return(true, nil).Once()
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, req.PhoneNumber).Return(true, nil).Once()
|
||||
mockRepo.EXPECT().GetAdminByPhoneNumber(ctx, req.PhoneNumber).Return(entity.Admin{
|
||||
Status: entity.AdminActiveStatus,
|
||||
}, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
|
@ -88,7 +98,7 @@ func TestValidateLoginWithPhoneNumberRequest(t *testing.T) {
|
|||
Password: validPassword,
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, validPhoneNumber).Return(false, nil).Once()
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, req.PhoneNumber).Return(false, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
|
@ -102,11 +112,28 @@ func TestValidateLoginWithPhoneNumberRequest(t *testing.T) {
|
|||
Password: validPassword,
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, validPhoneNumber).Return(false, errors.New("repo error")).Once()
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, req.PhoneNumber).Return(false, errors.New("repo error")).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Equal(t, errmsg.ErrorMsgSomethingWentWrong, fieldErrors["phone_number"])
|
||||
})
|
||||
|
||||
t.Run("Inactive admin is not allowed", func(t *testing.T) {
|
||||
req := adminserviceparam.LoginWithPhoneNumberRequest{
|
||||
PhoneNumber: validPhoneNumber,
|
||||
Password: validPassword,
|
||||
}
|
||||
|
||||
mockRepo.EXPECT().AdminExistByPhoneNumber(ctx, req.PhoneNumber).Return(true, nil).Once()
|
||||
mockRepo.EXPECT().GetAdminByPhoneNumber(ctx, req.PhoneNumber).Return(entity.Admin{
|
||||
Status: entity.AdminInactiveStatus,
|
||||
}, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateLoginWithPhoneNumberRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Equal(t, errmsg.ErrorMsgAdminNotAllowed, fieldErrors["phone_number"])
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package adminvalidator
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
entity "git.gocasts.ir/ebhomengo/niki/entity"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
|
@ -135,6 +136,63 @@ func (_c *MockRepository_AdminExistByPhoneNumber_Call) RunAndReturn(run func(con
|
|||
return _c
|
||||
}
|
||||
|
||||
// GetAdminByPhoneNumber provides a mock function with given fields: ctx, phoneNumber
|
||||
func (_m *MockRepository) GetAdminByPhoneNumber(ctx context.Context, phoneNumber string) (entity.Admin, error) {
|
||||
ret := _m.Called(ctx, phoneNumber)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetAdminByPhoneNumber")
|
||||
}
|
||||
|
||||
var r0 entity.Admin
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (entity.Admin, error)); ok {
|
||||
return rf(ctx, phoneNumber)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) entity.Admin); ok {
|
||||
r0 = rf(ctx, phoneNumber)
|
||||
} else {
|
||||
r0 = ret.Get(0).(entity.Admin)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, phoneNumber)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockRepository_GetAdminByPhoneNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAdminByPhoneNumber'
|
||||
type MockRepository_GetAdminByPhoneNumber_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetAdminByPhoneNumber is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - phoneNumber string
|
||||
func (_e *MockRepository_Expecter) GetAdminByPhoneNumber(ctx interface{}, phoneNumber interface{}) *MockRepository_GetAdminByPhoneNumber_Call {
|
||||
return &MockRepository_GetAdminByPhoneNumber_Call{Call: _e.mock.On("GetAdminByPhoneNumber", ctx, phoneNumber)}
|
||||
}
|
||||
|
||||
func (_c *MockRepository_GetAdminByPhoneNumber_Call) Run(run func(ctx context.Context, phoneNumber string)) *MockRepository_GetAdminByPhoneNumber_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRepository_GetAdminByPhoneNumber_Call) Return(_a0 entity.Admin, _a1 error) *MockRepository_GetAdminByPhoneNumber_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRepository_GetAdminByPhoneNumber_Call) RunAndReturn(run func(context.Context, string) (entity.Admin, error)) *MockRepository_GetAdminByPhoneNumber_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockRepository creates a new instance of MockRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockRepository(t interface {
|
||||
|
|
|
@ -3,7 +3,6 @@ package adminvalidator
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
|
@ -23,6 +22,7 @@ const (
|
|||
type Repository interface {
|
||||
AdminExistByPhoneNumber(ctx context.Context, phoneNumber string) (bool, error)
|
||||
AdminExistByEmail(ctx context.Context, email string) (bool, error)
|
||||
GetAdminByPhoneNumber(ctx context.Context, phoneNumber string) (entity.Admin, error)
|
||||
}
|
||||
|
||||
type Validator struct {
|
||||
|
@ -33,6 +33,24 @@ func New(repo Repository) Validator {
|
|||
return Validator{repo: repo}
|
||||
}
|
||||
|
||||
func (v Validator) isAdminAllowed(ctx context.Context) func(interface{}) error {
|
||||
return func(value interface{}) error {
|
||||
phoneNumber, ok := value.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
|
||||
}
|
||||
admin, err := v.repo.GetAdminByPhoneNumber(ctx, phoneNumber)
|
||||
if err != nil {
|
||||
return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
|
||||
}
|
||||
if admin.Status == entity.AdminInactiveStatus {
|
||||
return fmt.Errorf(errmsg.ErrorMsgAdminNotAllowed)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (v Validator) doesAdminExistByPhoneNumber(ctx context.Context) validation.RuleFunc {
|
||||
return func(value interface{}) error {
|
||||
phoneNumber, ok := value.(string)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.45.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package adminkindboxvalidator
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.45.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package adminkindboxvalidator
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.45.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package adminkindboxvalidator
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.45.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package adminkindboxvalidator
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.45.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package adminkindboxvalidator
|
||||
|
||||
|
|
|
@ -38,6 +38,11 @@ func (v Validator) ValidateAddRequest(ctx context.Context, req param.KindBoxReqA
|
|||
validation.Required,
|
||||
validation.By(v.isDateValid),
|
||||
),
|
||||
|
||||
validation.Field(&req.DeliverReferTimeID,
|
||||
validation.Required,
|
||||
validation.By(v.isReferTimeIDValid(ctx)),
|
||||
),
|
||||
); err != nil {
|
||||
|
||||
fieldErrors := make(map[string]string)
|
||||
|
@ -112,6 +117,7 @@ func (v Validator) isDateValid(value interface{}) error {
|
|||
if !ok {
|
||||
return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
|
||||
}
|
||||
|
||||
if date.Before(time.Now()) {
|
||||
return fmt.Errorf(errmsg.ErrorMsgInvalidInput)
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ func (v Validator) ValidateUpdateRequest(ctx context.Context, req param.KindBoxR
|
|||
validation.Max(uint(MaxKindBoxReq)),
|
||||
),
|
||||
validation.Field(&req.CountAccepted,
|
||||
validation.Min(MinKindBoxReq),
|
||||
validation.Max(MaxKindBoxReq),
|
||||
validation.Min(uint(MinKindBoxReq)),
|
||||
validation.Max(uint(MaxKindBoxReq)),
|
||||
validation.When(req.CountRequested > 0, validation.Max(req.CountRequested)),
|
||||
validation.By(v.checkCountAcceptedMustBeLessThanCountRequested(ctx, req.ID)),
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactoraddressvalidator
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
context "context"
|
||||
|
||||
benefactoreparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/benefactor"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactoraddressvalidator
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ import (
|
|||
)
|
||||
|
||||
func TestValidator_ValidateLoginRegisterRequest(t *testing.T) {
|
||||
validator := New()
|
||||
mockRepository := NewMockRepository(t)
|
||||
validator := New(mockRepository)
|
||||
validPhoneNumber := "09123456789"
|
||||
|
||||
t.Run("Valid request", func(t *testing.T) {
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactorvalidator
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
entity "git.gocasts.ir/ebhomengo/niki/entity"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// MockRepository is an autogenerated mock type for the Repository type
|
||||
type MockRepository struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockRepository_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockRepository) EXPECT() *MockRepository_Expecter {
|
||||
return &MockRepository_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetByPhoneNumber provides a mock function with given fields: ctx, phoneNumber
|
||||
func (_m *MockRepository) GetByPhoneNumber(ctx context.Context, phoneNumber string) (entity.Benefactor, error) {
|
||||
ret := _m.Called(ctx, phoneNumber)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetByPhoneNumber")
|
||||
}
|
||||
|
||||
var r0 entity.Benefactor
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (entity.Benefactor, error)); ok {
|
||||
return rf(ctx, phoneNumber)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) entity.Benefactor); ok {
|
||||
r0 = rf(ctx, phoneNumber)
|
||||
} else {
|
||||
r0 = ret.Get(0).(entity.Benefactor)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, phoneNumber)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockRepository_GetByPhoneNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByPhoneNumber'
|
||||
type MockRepository_GetByPhoneNumber_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetByPhoneNumber is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - phoneNumber string
|
||||
func (_e *MockRepository_Expecter) GetByPhoneNumber(ctx interface{}, phoneNumber interface{}) *MockRepository_GetByPhoneNumber_Call {
|
||||
return &MockRepository_GetByPhoneNumber_Call{Call: _e.mock.On("GetByPhoneNumber", ctx, phoneNumber)}
|
||||
}
|
||||
|
||||
func (_c *MockRepository_GetByPhoneNumber_Call) Run(run func(ctx context.Context, phoneNumber string)) *MockRepository_GetByPhoneNumber_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRepository_GetByPhoneNumber_Call) Return(_a0 entity.Benefactor, _a1 error) *MockRepository_GetByPhoneNumber_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRepository_GetByPhoneNumber_Call) RunAndReturn(run func(context.Context, string) (entity.Benefactor, error)) *MockRepository_GetByPhoneNumber_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockRepository creates a new instance of MockRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockRepository(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockRepository {
|
||||
mock := &MockRepository{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package benefactorvalidator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"regexp"
|
||||
|
||||
|
@ -10,13 +11,15 @@ import (
|
|||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
)
|
||||
|
||||
func (v Validator) ValidateSendOtpRequest(req benefactoreparam.SendOtpRequest) (map[string]string, error) {
|
||||
func (v Validator) ValidateSendOtpRequest(ctx context.Context, req benefactoreparam.SendOtpRequest) (map[string]string, error) {
|
||||
const op = "benefactorvalidator.ValidateSendOtpRequest"
|
||||
|
||||
if err := validation.ValidateStruct(&req,
|
||||
validation.Field(&req.PhoneNumber,
|
||||
validation.Required,
|
||||
validation.Match(regexp.MustCompile(phoneNumberRegex)).Error(errmsg.ErrorMsgPhoneNumberIsNotValid))); err != nil {
|
||||
validation.Match(regexp.MustCompile(phoneNumberRegex)).Error(errmsg.ErrorMsgPhoneNumberIsNotValid),
|
||||
validation.By(v.isBenefactorAllowed(ctx)),
|
||||
)); err != nil {
|
||||
fieldErrors := make(map[string]string)
|
||||
|
||||
vErr := validation.Errors{}
|
||||
|
|
|
@ -1,23 +1,32 @@
|
|||
package benefactorvalidator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"context"
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
benefactoreparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/benefactor"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidator_ValidateSendOtpRequest(t *testing.T) {
|
||||
validator := New()
|
||||
mockRepository := NewMockRepository(t)
|
||||
validator := New(mockRepository)
|
||||
|
||||
validPhoneNumber := "09123456789"
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("Valid request", func(t *testing.T) {
|
||||
req := benefactoreparam.SendOtpRequest{
|
||||
PhoneNumber: validPhoneNumber,
|
||||
}
|
||||
|
||||
fieldErrors, err := validator.ValidateSendOtpRequest(req)
|
||||
mockRepository.EXPECT().GetByPhoneNumber(ctx, req.PhoneNumber).Return(entity.Benefactor{
|
||||
Status: entity.BenefactorActiveStatus,
|
||||
}, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateSendOtpRequest(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, fieldErrors)
|
||||
})
|
||||
|
@ -27,7 +36,7 @@ func TestValidator_ValidateSendOtpRequest(t *testing.T) {
|
|||
PhoneNumber: "",
|
||||
}
|
||||
|
||||
fieldErrors, err := validator.ValidateSendOtpRequest(req)
|
||||
fieldErrors, err := validator.ValidateSendOtpRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Contains(t, fieldErrors, "phone_number")
|
||||
|
@ -38,9 +47,37 @@ func TestValidator_ValidateSendOtpRequest(t *testing.T) {
|
|||
PhoneNumber: "12345",
|
||||
}
|
||||
|
||||
fieldErrors, err := validator.ValidateSendOtpRequest(req)
|
||||
fieldErrors, err := validator.ValidateSendOtpRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Equal(t, errmsg.ErrorMsgPhoneNumberIsNotValid, fieldErrors["phone_number"])
|
||||
})
|
||||
|
||||
t.Run("Inactive user is not allowed", func(t *testing.T) {
|
||||
req := benefactoreparam.SendOtpRequest{
|
||||
PhoneNumber: validPhoneNumber,
|
||||
}
|
||||
|
||||
mockRepository.EXPECT().GetByPhoneNumber(ctx, req.PhoneNumber).Return(entity.Benefactor{
|
||||
Status: entity.BenefactorInactiveStatus,
|
||||
}, nil).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateSendOtpRequest(ctx, req)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, fieldErrors)
|
||||
assert.Equal(t, errmsg.ErrorMsgUserNotAllowed, fieldErrors["phone_number"])
|
||||
})
|
||||
|
||||
t.Run("new users are allowed", func(t *testing.T) {
|
||||
req := benefactoreparam.SendOtpRequest{
|
||||
PhoneNumber: validPhoneNumber,
|
||||
}
|
||||
|
||||
mockRepository.EXPECT().GetByPhoneNumber(ctx, req.PhoneNumber).Return(entity.Benefactor{}, richerror.New("test").
|
||||
WithKind(richerror.KindNotFound)).Once()
|
||||
|
||||
fieldErrors, err := validator.ValidateSendOtpRequest(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, fieldErrors)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,11 +1,51 @@
|
|||
package benefactorvalidator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.gocasts.ir/ebhomengo/niki/entity"
|
||||
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
||||
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
||||
)
|
||||
|
||||
const (
|
||||
phoneNumberRegex = "^09\\d{9}$"
|
||||
)
|
||||
|
||||
type Validator struct{}
|
||||
|
||||
func New() Validator {
|
||||
return Validator{}
|
||||
//go:generate mockery --name Repository
|
||||
type Repository interface {
|
||||
GetByPhoneNumber(ctx context.Context, phoneNumber string) (entity.Benefactor, error)
|
||||
}
|
||||
|
||||
type Validator struct {
|
||||
repo Repository
|
||||
}
|
||||
|
||||
func New(repo Repository) Validator {
|
||||
return Validator{repo: repo}
|
||||
}
|
||||
|
||||
func (v Validator) isBenefactorAllowed(ctx context.Context) func(interface{}) error {
|
||||
return func(value interface{}) error {
|
||||
phoneNumber, ok := value.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
|
||||
}
|
||||
bnf, err := v.repo.GetByPhoneNumber(ctx, phoneNumber)
|
||||
if err != nil {
|
||||
// new users are always allowed
|
||||
var richErr richerror.RichError
|
||||
if errors.As(err, &richErr) && richErr.Kind() == richerror.KindNotFound {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
|
||||
}
|
||||
if bnf.Status == entity.BenefactorInactiveStatus {
|
||||
return fmt.Errorf(errmsg.ErrorMsgUserNotAllowed)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactorkindboxvalidator
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
context "context"
|
||||
|
||||
addressparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/address"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactorkindboxvalidator
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
context "context"
|
||||
|
||||
benefactoreparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/benefactor"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactorkindboxvalidator
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactorkindboxvalidator
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactorkindboxreqvalidator
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
context "context"
|
||||
|
||||
addressparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/address"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactorkindboxreqvalidator
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
context "context"
|
||||
|
||||
benefactoreparam "git.gocasts.ir/ebhomengo/niki/param/benefactor/benefactor"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactorkindboxreqvalidator
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v2.41.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.45.1. DO NOT EDIT.
|
||||
|
||||
package benefactorkindboxreqvalidator
|
||||
|
||||
|
|
Loading…
Reference in New Issue