diff --git a/.env.test b/.env.test index ab8355f..3f8c716 100644 --- a/.env.test +++ b/.env.test @@ -3,40 +3,18 @@ HTTP_HOST=0.0.0.0 HTTP_PORT=8080 # MONGO settings -MONGO_HOST=mongo +MONGO_HOST=localhost MONGO_PORT=27017 MONGO_USER=test MONGO_PASSWORD=test MONGO_AUTH=admin MONGO_DB_NAME=admin -# GOOGLE settings -GOOGLE_REDIRECT_URL=http://localhost:8080/google/callback -GOOGLE_CLIENT_ID=test.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=test-0VOAAx87vr7epBTG -GOOGLE_OAUTH_HOST=https://www.googleapis.com/oauth2/v3 - -# VK settings -VK_REDIRECT_URL=http://localhost:8080/vk/callback -VK_CLIENT_ID=51394112 -VK_CLIENT_SECRET=test_ursJCpEY - -# Amocrm settings -AMOCRM_CLIENT_ID=d677e851-03e0-467e-a5de-2ebcf6d47529 -AMOCRM_CLIENT_SECRET=fOzWUMyF6fGmAkgLULN0H4wFUClVTsaVTPOmVazit4cQzuDM1JhGr3MP6IZIcKmj -AMOCRM_REDIRECT_URL=https://oauth.pena.digital/amocrm/callback -AMOCRM_OAUTH_HOST=https://www.amocrm.ru/oauth -AMOCRM_USER_INFO_URL=http://localhost:8000/api/v4/account -AMOCRM_ACCESS_TOKEN_URL=http://localhost:8000/oauth2/access_token - # Auth Microservice settings -AUTH_MICROSERVICE_GROUP=group -AUTH_MICROSERVICE_PRIVATE_SIGN_KEY="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIKn0BKwF3vZvODgWAnUIwQhd8de5oZhY48gc23EWfrfs\n-----END PRIVATE KEY-----" -AUTH_MICROSERVICE_EXHANGE_URL=http://localhost:8000/exchange -AUTH_MICROSERVICE_REGISTER_URL=http://localhost:8000/register AUTH_MICROSERVICE_USER_URL=http://localhost:8000/user # JWT settings -JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyt4XuLovUY7i12K2PIMbQZOKn+wFFKUvxvKQDel049/+VMpHMx1FLolUKuyGp9zi6gOwjHsBPgc9oqr/eaXGQSh7Ult7i9f+Ht563Y0er5UU9Zc5ZPSxf9O75KYD48ruGkqiFoncDqPENK4dtUa7w0OqlN4bwVBbmIsP8B3EDC5Dof+vtiNTSHSXPx+zifKeZGyknp+nyOHVrRDhPjOhzQzCom0MSZA/sJYmps8QZgiPA0k4Z6jTupDymPOIwYeD2C57zSxnAv0AfC3/pZYJbZYH/0TszRzmy052DME3zMnhMK0ikdN4nzYqU0dkkA5kb5GtKDymspHIJ9eWbUuwgtg8Rq/LrVBj1I3UFgs0ibio40k6gqinLKslc5Y1I5mro7J3OSEP5eO/XeDLOLlOJjEqkrx4fviI1cL3m5L6QV905xmcoNZG1+RmOg7D7cZQUf27TXqM381jkbNdktm1JLTcMScxuo3vaRftnIVw70V8P8sIkaKY8S8HU1sQgE2LB9t04oog5u59htx2FHv4B13NEm8tt8Tv1PexpB4UVh7PIualF6SxdFBrKbraYej72wgjXVPQ0eGXtGGD57j8DUEzk7DK2OvIWhehlVqtiRnFdAvdBj2ynHT2/5FJ/Zpd4n5dKGJcQvy1U1qWMs+8M7AHfWyt2+nZ04s48+bK3yMCAwEAAQ==\n-----END PUBLIC KEY-----" -JWT_ISSUER="issuer" -JWT_AUDIENCE="audience" \ No newline at end of file +JWT_ISSUER="pena-auth-service" +JWT_AUDIENCE="pena" + +JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAE=\n-----END PUBLIC KEY-----" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 021ec94..3e618e6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,7 +9,8 @@ lint: image: golangci/golangci-lint:v1.52-alpine stage: lint before_script: - - go install github.com/vektra/mockery/v2@v2.26.0 + - go install github.com/vektra/mockery/v2@v2.26.0 + - go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4 script: - go generate ./... - golangci-lint version diff --git a/go.mod b/go.mod index 10c09b5..d6f697d 100644 --- a/go.mod +++ b/go.mod @@ -19,11 +19,16 @@ require ( require ( cloud.google.com/go/compute/metadata v0.2.0 // indirect + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/PuerkitoBio/purell v1.2.0 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/brpaz/echozap v1.1.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/swag v0.21.1 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/spec v0.20.9 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect @@ -43,6 +48,7 @@ require ( github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/swaggo/swag v1.16.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect @@ -54,10 +60,11 @@ require ( go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.9.0 // indirect golang.org/x/net v0.10.0 // indirect - golang.org/x/sync v0.1.0 // indirect + golang.org/x/sync v0.2.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.9.1 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index 247b8fb..f0fc7f5 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,11 @@ cloud.google.com/go/compute/metadata v0.2.0 h1:nBbNSZyDpkNlo3DepaaLKVuO7ClyifSAmNloSCZrHnQ= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/PuerkitoBio/purell v1.2.0 h1:/Jdm5QfyM8zdlqT6WVZU4cfP23sot6CEHA4CS49Ezig= +github.com/PuerkitoBio/purell v1.2.0/go.mod h1:OhLRTaaIzhvIyofkJfB24gokC7tM42Px5UhoT32THBk= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= @@ -17,11 +23,22 @@ github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGn github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/getkin/kin-openapi v0.116.0 h1:o986hwgMzR972JzOG5j6+WTwWqllZLs1EJKMKCivs2E= github.com/getkin/kin-openapi v0.116.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= +github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= @@ -118,6 +135,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg= +github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= @@ -169,6 +188,8 @@ golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -196,6 +217,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= diff --git a/internal/app/app.go b/internal/app/app.go index 3e59409..71723b9 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -40,7 +40,6 @@ func Run(ctx context.Context, config *models.Config, logger *zap.Logger) error { services := initialize.NewServices(&initialize.ServicesDeps{ Logger: logger, - Config: &config.Service, Repositories: repositories, Clients: clients, }) @@ -50,21 +49,21 @@ func Run(ctx context.Context, config *models.Config, logger *zap.Logger) error { Services: services, }) - openAPI, err := swagger.GetSwagger() + openapi, err := swagger.GetSwagger() if err != nil { return fmt.Errorf("failed to loading openapi spec: %w", err) } - swagger := initialize.NewSwagger(controllers) + api := initialize.NewAPI(controllers) closer := closer.New() httpServer := server.New(&server.Deps{ - Logger: logger, - Swagger: openAPI, - JWTUtil: utils.NewJWT[models.AuthJWTDecoded](&config.Service.JWT), + Logger: logger, + Swagger: openapi, + AuthenticationFunc: utils.NewAuthenticator(utils.NewJWT(&config.Service.JWT)), }) - httpServer.Register(swagger) + httpServer.Register(api) go httpServer.Run(&config.HTTP) diff --git a/internal/controller/account/account.go b/internal/controller/account/account.go index cc71cb5..b64533d 100644 --- a/internal/controller/account/account.go +++ b/internal/controller/account/account.go @@ -15,7 +15,7 @@ import ( type accountService interface { GetAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) - GetAccountsList(ctx context.Context, pagination *models.Pagination) ([]models.Account, errors.Error) + GetAccountsList(ctx context.Context, pagination *models.Pagination) (*models.PaginationResponse[models.Account], errors.Error) CreateAccount(ctx context.Context, account *models.Account) (*models.Account, errors.Error) CreateAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) RemoveAccount(ctx context.Context, userID string) (*models.Account, errors.Error) @@ -56,8 +56,8 @@ func (receiver *Controller) GetAccount(ctx echo.Context) error { return ctx.JSON(http.StatusOK, account) } -func (receiver *Controller) GetDirectAccount(ctx echo.Context, userId string) error { - account, err := receiver.service.GetAccountByUserID(ctx.Request().Context(), userId) +func (receiver *Controller) GetDirectAccount(ctx echo.Context, userID string) error { + account, err := receiver.service.GetAccountByUserID(ctx.Request().Context(), userID) if err != nil { return utils.DetermineEchoErrorResponse(ctx, err) } @@ -66,7 +66,7 @@ func (receiver *Controller) GetDirectAccount(ctx echo.Context, userId string) er } func (receiver *Controller) GetAccounts(ctx echo.Context, params swagger.PaginationAccountsParams) error { - accounts, err := receiver.service.GetAccountsList( + response, err := receiver.service.GetAccountsList( ctx.Request().Context(), utils.DeterminePagination(params.Page, params.Limit), ) @@ -74,7 +74,7 @@ func (receiver *Controller) GetAccounts(ctx echo.Context, params swagger.Paginat return utils.DetermineEchoErrorResponse(ctx, err) } - return ctx.JSON(http.StatusOK, accounts) + return ctx.JSON(http.StatusOK, response) } func (receiver *Controller) CreateAccount(ctx echo.Context) error { @@ -111,8 +111,8 @@ func (receiver *Controller) RemoveAccount(ctx echo.Context) error { return ctx.JSON(http.StatusOK, account) } -func (receiver *Controller) RemoveDirectAccount(ctx echo.Context, userId string) error { - account, err := receiver.service.RemoveAccount(ctx.Request().Context(), userId) +func (receiver *Controller) RemoveDirectAccount(ctx echo.Context, userID string) error { + account, err := receiver.service.RemoveAccount(ctx.Request().Context(), userID) if err != nil { return utils.DetermineEchoErrorResponse(ctx, err) } diff --git a/internal/errors/errors.go b/internal/errors/errors.go index e9555af..360bd1e 100644 --- a/internal/errors/errors.go +++ b/internal/errors/errors.go @@ -13,6 +13,7 @@ var ( ErrMethodNotImplemented ErrorType = errors.New("method is not implemented") ErrNotFound ErrorType = errors.New("record not found") ErrNoAccess ErrorType = errors.New("no access") + ErrConflict ErrorType = errors.New("record already exist") ) type Error interface { diff --git a/internal/initialize/api.go b/internal/initialize/api.go new file mode 100644 index 0000000..6d9d912 --- /dev/null +++ b/internal/initialize/api.go @@ -0,0 +1,9 @@ +package initialize + +import "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/swagger" + +func NewAPI(controllers *Controllers) *swagger.API { + return swagger.New(&swagger.Deps{ + AccountController: controllers.AccountController, + }) +} diff --git a/internal/initialize/api_test.go b/internal/initialize/api_test.go new file mode 100644 index 0000000..663a502 --- /dev/null +++ b/internal/initialize/api_test.go @@ -0,0 +1,49 @@ +package initialize_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/mongo/integration/mtest" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +func TestNewAPI(t *testing.T) { + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + + mt.Run("API сваггера должен успешно инициализироваться", func(t *mtest.T) { + assert.NotPanics(t, func() { + logger := zap.New(zap.L().Core()) + + repositories := initialize.NewRepositories(&initialize.RepositoriesDeps{ + Logger: logger, + MongoDB: t.Client.Database("test"), + }) + + clients := initialize.NewClients(&initialize.ClientsDeps{ + Logger: logger, + AuthURL: &models.AuthMicroServiceURL{ + User: "", + }, + }) + + services := initialize.NewServices(&initialize.ServicesDeps{ + Logger: logger, + Repositories: repositories, + Clients: clients, + }) + + controllers := initialize.NewControllers(&initialize.ControllersDeps{ + Logger: logger, + Services: services, + }) + + api := initialize.NewAPI(controllers) + + assert.NotNil(t, api) + assert.NotNil(t, api.AccountController) + }) + }) +} diff --git a/internal/initialize/config_test.go b/internal/initialize/config_test.go index 21eb23d..30da0ee 100644 --- a/internal/initialize/config_test.go +++ b/internal/initialize/config_test.go @@ -11,6 +11,40 @@ import ( "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo" ) +func TestConfiguration(t *testing.T) { + t.Run("Успешная инициализация конфигурации", func(t *testing.T) { + defaultConfiguration := setDefaultTestingENV(t) + + assert.NotPanics(t, func() { + configuration, err := initialize.Configuration("") + + assert.NoError(t, err) + assert.Equal(t, defaultConfiguration, configuration) + }) + }) + + t.Run("Ошибка при наличии кривого url", func(t *testing.T) { + setDefaultTestingENV(t) + t.Setenv("AUTH_MICROSERVICE_USER_URL", "url") + + assert.NotPanics(t, func() { + configuration, err := initialize.Configuration("") + + assert.Error(t, err) + assert.Nil(t, configuration) + }) + }) + + t.Run("Ошибка при отсутствии обязательного env", func(t *testing.T) { + assert.NotPanics(t, func() { + configuration, err := initialize.Configuration("") + + assert.Error(t, err) + assert.Nil(t, configuration) + }) + }) +} + func setDefaultTestingENV(t *testing.T) *models.Config { t.Helper() @@ -62,37 +96,3 @@ func setDefaultTestingENV(t *testing.T) *models.Config { return &defaultConfiguration } - -func TestConfiguration(t *testing.T) { - t.Run("Успешная инициализация конфигурации", func(t *testing.T) { - defaultConfiguration := setDefaultTestingENV(t) - - assert.NotPanics(t, func() { - configuration, err := initialize.Configuration("") - - assert.NoError(t, err) - assert.Equal(t, defaultConfiguration, configuration) - }) - }) - - t.Run("Ошибка при наличии кривого url", func(t *testing.T) { - setDefaultTestingENV(t) - t.Setenv("AMOCRM_USER_INFO_URL", "url") - - assert.NotPanics(t, func() { - configuration, err := initialize.Configuration("") - - assert.Error(t, err) - assert.Nil(t, configuration) - }) - }) - - t.Run("Ошибка при отсутствии обязательного env", func(t *testing.T) { - assert.NotPanics(t, func() { - configuration, err := initialize.Configuration("") - - assert.Error(t, err) - assert.Nil(t, configuration) - }) - }) -} diff --git a/internal/initialize/controllers_test.go b/internal/initialize/controllers_test.go index d962686..cf001a3 100644 --- a/internal/initialize/controllers_test.go +++ b/internal/initialize/controllers_test.go @@ -15,6 +15,7 @@ func TestNewControllers(t *testing.T) { }) assert.NotNil(t, controllers) + assert.NotNil(t, controllers.AccountController) }) }) } diff --git a/internal/initialize/services.go b/internal/initialize/services.go index 47b93d3..670e0ac 100644 --- a/internal/initialize/services.go +++ b/internal/initialize/services.go @@ -2,13 +2,11 @@ package initialize import ( "go.uber.org/zap" - "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/account" ) type ServicesDeps struct { Logger *zap.Logger - Config *models.ServiceConfiguration Repositories *Repositories Clients *Clients } @@ -22,6 +20,7 @@ func NewServices(deps *ServicesDeps) *Services { AccountService: account.New(&account.Deps{ Logger: deps.Logger, Repository: deps.Repositories.AccountRepository, + AuthClient: deps.Clients.AuthClient, }), } } diff --git a/internal/initialize/services_test.go b/internal/initialize/services_test.go index 64d4468..25843d4 100644 --- a/internal/initialize/services_test.go +++ b/internal/initialize/services_test.go @@ -4,21 +4,28 @@ import ( "testing" "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/mongo/integration/mtest" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" ) func TestNewServices(t *testing.T) { - configuration := setDefaultTestingENV(t) + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) - t.Run("Сервисы должны успешно инициализироваться", func(t *testing.T) { + mt.Run("Сервисы должны успешно инициализироваться", func(t *mtest.T) { assert.NotPanics(t, func() { + clients := initialize.NewClients(&initialize.ClientsDeps{}) + repositories := initialize.NewRepositories(&initialize.RepositoriesDeps{ + MongoDB: t.Client.Database("test"), + }) services := initialize.NewServices(&initialize.ServicesDeps{ - Config: &configuration.Service, - Clients: &initialize.Clients{}, - Repositories: &initialize.Repositories{}, + Clients: clients, + Repositories: repositories, }) assert.NotNil(t, services) + assert.NotNil(t, services.AccountService) + assert.NotNil(t, services.AccountService.AuthClient) + assert.NotNil(t, services.AccountService.Repository) }) }) } diff --git a/internal/initialize/swagger.go b/internal/initialize/swagger.go deleted file mode 100644 index 573cfa2..0000000 --- a/internal/initialize/swagger.go +++ /dev/null @@ -1,7 +0,0 @@ -package initialize - -import "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/swagger" - -func NewSwagger(controllers *Controllers) *swagger.API { - return swagger.New(&swagger.Deps{}) -} diff --git a/internal/models/account.go b/internal/models/account.go index 28fe83f..5f423de 100644 --- a/internal/models/account.go +++ b/internal/models/account.go @@ -3,7 +3,7 @@ package models import "time" type Account struct { - ID string `json:"id" bson:"_id"` + ID string `json:"id" bson:"_id,omitempty"` UserID string `json:"userId" bson:"userId"` Cart []string `json:"cart" bson:"cart"` Wallet Wallet `json:"wallet" bson:"wallet"` diff --git a/internal/models/auth.go b/internal/models/auth.go index 15e27e3..9322913 100644 --- a/internal/models/auth.go +++ b/internal/models/auth.go @@ -13,8 +13,4 @@ type User struct { DeletedAt *time.Time `json:"deletedAt,omitempty"` } -type AuthJWTDecoded struct { - UserID string `json:"id"` -} - const AuthJWTDecodedUserIDKey = "userID" diff --git a/internal/models/common.go b/internal/models/common.go index e3c53d1..98b3977 100644 --- a/internal/models/common.go +++ b/internal/models/common.go @@ -11,6 +11,11 @@ type ResponseErrorHTTP struct { Message string `json:"message"` } +type PaginationResponse[T any] struct { + TotalPages int64 `json:"totalPages"` + Records []T `json:"records"` +} + type Pagination struct { Page int64 Limit int64 diff --git a/internal/repository/account.go b/internal/repository/account.go index 3d93464..8e2f878 100644 --- a/internal/repository/account.go +++ b/internal/repository/account.go @@ -177,3 +177,19 @@ func (receiver *AccountRepository) Delete(ctx context.Context, id string) (*mode return &account, nil } + +func (receiver *AccountRepository) CountAll(ctx context.Context) (int64, errors.Error) { + count, err := receiver.mongoDB.CountDocuments(ctx, bson.M{fields.Account.Deleted: false}) + if err != nil { + receiver.logger.Error("failed to count all documents on of ", + zap.Error(err), + ) + + return 0, errors.New( + fmt.Errorf("failed to count all documents on of : %w", err), + errors.ErrInternalError, + ) + } + + return count, nil +} diff --git a/internal/server/http.go b/internal/server/http.go index cc496aa..e7a9d30 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -6,8 +6,6 @@ import ( "net/http" "time" - "github.com/brpaz/echozap" - oapiMiddleware "github.com/deepmap/oapi-codegen/pkg/middleware" "github.com/getkin/kin-openapi/openapi3" "github.com/getkin/kin-openapi/openapi3filter" "github.com/labstack/echo/v4" @@ -15,13 +13,12 @@ import ( "go.uber.org/zap" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/swagger" - "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" ) type Deps struct { - Logger *zap.Logger - Swagger *openapi3.T - JWTUtil *utils.JWT[models.AuthJWTDecoded] + Logger *zap.Logger + Swagger *openapi3.T + AuthenticationFunc openapi3filter.AuthenticationFunc } type HTTP struct { @@ -33,12 +30,8 @@ type HTTP struct { func New(deps *Deps) *HTTP { echo := echo.New() - echo.Use(echozap.ZapLogger(deps.Logger)) echo.Use(middleware.Recover()) - echo.Use(oapiMiddleware.OapiRequestValidator(deps.Swagger)) - echo.Use(oapiMiddleware.OapiRequestValidatorWithOptions(deps.Swagger, &oapiMiddleware.Options{ - Options: openapi3filter.Options{AuthenticationFunc: utils.NewAuthenticator(deps.JWTUtil)}, - })) + echo.Use(swagger.CreateMiddleware(deps.Swagger, deps.AuthenticationFunc)) return &HTTP{ echo: echo, diff --git a/internal/service/account/account.go b/internal/service/account/account.go index f12e139..6326e80 100644 --- a/internal/service/account/account.go +++ b/internal/service/account/account.go @@ -3,6 +3,7 @@ package account import ( "context" "fmt" + "math" "go.uber.org/zap" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" @@ -15,6 +16,7 @@ type accountRepository interface { Insert(ctx context.Context, account *models.Account) (*models.Account, errors.Error) Remove(ctx context.Context, id string) (*models.Account, errors.Error) Delete(ctx context.Context, id string) (*models.Account, errors.Error) + CountAll(ctx context.Context) (int64, errors.Error) } type authClient interface { @@ -29,20 +31,20 @@ type Deps struct { type Service struct { logger *zap.Logger - repository accountRepository - authClient authClient + Repository accountRepository + AuthClient authClient } func New(deps *Deps) *Service { return &Service{ logger: deps.Logger, - repository: deps.Repository, - authClient: deps.AuthClient, + Repository: deps.Repository, + AuthClient: deps.AuthClient, } } func (receiver *Service) GetAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) { - account, err := receiver.repository.FindByUserID(ctx, userID) + account, err := receiver.Repository.FindByUserID(ctx, userID) if err != nil { receiver.logger.Error("failed to get account by id on of ", zap.Error(err.Extract()), @@ -55,7 +57,7 @@ func (receiver *Service) GetAccountByUserID(ctx context.Context, userID string) return account, nil } -func (receiver *Service) GetAccountsList(ctx context.Context, pagination *models.Pagination) ([]models.Account, errors.Error) { +func (receiver *Service) GetAccountsList(ctx context.Context, pagination *models.Pagination) (*models.PaginationResponse[models.Account], errors.Error) { if pagination == nil { return nil, errors.New( fmt.Errorf("pagination is nil on of : %w", errors.ErrInternalError), @@ -63,7 +65,22 @@ func (receiver *Service) GetAccountsList(ctx context.Context, pagination *models ) } - accounts, err := receiver.repository.FindMany(ctx, pagination.Page, pagination.Limit) + count, err := receiver.Repository.CountAll(ctx) + if err != nil { + receiver.logger.Error("failed to count accounts on of ", + zap.Error(err.Extract()), + ) + + return nil, err + } + + if count == 0 { + return &models.PaginationResponse[models.Account]{TotalPages: 0, Records: []models.Account{}}, nil + } + + totalPages := int64(math.Ceil(float64(count) / float64(pagination.Limit))) + + accounts, err := receiver.Repository.FindMany(ctx, pagination.Page, pagination.Limit) if err != nil { receiver.logger.Error("failed to get accounts list on of ", zap.Error(err.Extract()), @@ -74,11 +91,33 @@ func (receiver *Service) GetAccountsList(ctx context.Context, pagination *models return nil, err } - return accounts, nil + return &models.PaginationResponse[models.Account]{ + TotalPages: totalPages, + Records: accounts, + }, nil } func (receiver *Service) CreateAccount(ctx context.Context, account *models.Account) (*models.Account, errors.Error) { - accounts, err := receiver.repository.Insert(ctx, account) + findedAccount, err := receiver.GetAccountByUserID(ctx, account.UserID) + if err != nil && err.Type() != errors.ErrNotFound { + receiver.logger.Error("failed to find account on of ", + zap.Error(err.Extract()), + ) + + return nil, err + } + + if findedAccount != nil { + return nil, errors.New( + fmt.Errorf("failed to create account with <%s> on of : %w", + account.UserID, + errors.ErrConflict, + ), + errors.ErrConflict, + ) + } + + createdAccount, err := receiver.Repository.Insert(ctx, account) if err != nil { receiver.logger.Error("failed to create account on of ", zap.Error(err.Extract()), @@ -88,16 +127,35 @@ func (receiver *Service) CreateAccount(ctx context.Context, account *models.Acco return nil, err } - return accounts, nil + return createdAccount, nil } /* -CreateAccountByUserID +CreateAccountByUserID возвращает аккаут по id пользователя -TODO: Дополнить проверку на дефолтное значение Currency +TODO: Дополнить проверку на дефолтное значение Currency. */ func (receiver *Service) CreateAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) { - user, err := receiver.authClient.GetUser(ctx, userID) + account, err := receiver.GetAccountByUserID(ctx, userID) + if err != nil && err.Type() != errors.ErrNotFound { + receiver.logger.Error("failed to find account on of ", + zap.Error(err.Extract()), + ) + + return nil, err + } + + if account != nil { + return nil, errors.New( + fmt.Errorf("failed to create account with <%s> on of : %w", + userID, + errors.ErrConflict, + ), + errors.ErrConflict, + ) + } + + user, err := receiver.AuthClient.GetUser(ctx, userID) if err != nil { receiver.logger.Error("failed to get user on of ", zap.Error(err.Extract()), @@ -107,7 +165,7 @@ func (receiver *Service) CreateAccountByUserID(ctx context.Context, userID strin return nil, err } - account, err := receiver.repository.Insert(ctx, &models.Account{ + createdAccount, err := receiver.Repository.Insert(ctx, &models.Account{ UserID: user.ID, Cart: make([]string, 0), Wallet: models.Wallet{ @@ -125,11 +183,11 @@ func (receiver *Service) CreateAccountByUserID(ctx context.Context, userID strin return nil, err } - return account, nil + return createdAccount, nil } func (receiver *Service) RemoveAccount(ctx context.Context, userID string) (*models.Account, errors.Error) { - account, err := receiver.repository.Remove(ctx, userID) + account, err := receiver.Repository.Remove(ctx, userID) if err != nil { receiver.logger.Error("failed to remove account on of ", zap.Error(err.Extract()), @@ -143,7 +201,7 @@ func (receiver *Service) RemoveAccount(ctx context.Context, userID string) (*mod } func (receiver *Service) DeleteAccount(ctx context.Context, userID string) (*models.Account, errors.Error) { - account, err := receiver.repository.Delete(ctx, userID) + account, err := receiver.Repository.Delete(ctx, userID) if err != nil { receiver.logger.Error("failed to delete account on of ", zap.Error(err.Extract()), diff --git a/internal/swagger/api.go b/internal/swagger/api.go index 99a2b47..f83117a 100644 --- a/internal/swagger/api.go +++ b/internal/swagger/api.go @@ -13,8 +13,8 @@ type AccountController interface { RemoveAccount(ctx echo.Context) error GetAccount(ctx echo.Context) error CreateAccount(ctx echo.Context) error - RemoveDirectAccount(ctx echo.Context, userId string) error - GetDirectAccount(ctx echo.Context, userId string) error + RemoveDirectAccount(ctx echo.Context, userID string) error + GetDirectAccount(ctx echo.Context, userID string) error GetAccounts(ctx echo.Context, params PaginationAccountsParams) error } @@ -23,37 +23,37 @@ type Deps struct { } type API struct { - accountController AccountController + AccountController AccountController } func New(deps *Deps) *API { return &API{ - accountController: deps.AccountController, + AccountController: deps.AccountController, } } func (receiver *API) DeleteAccount(ctx echo.Context) error { - return receiver.accountController.RemoveAccount(ctx) + return receiver.AccountController.RemoveAccount(ctx) } func (receiver *API) GetAccount(ctx echo.Context) error { - return receiver.accountController.GetAccount(ctx) + return receiver.AccountController.GetAccount(ctx) } func (receiver *API) AddAccount(ctx echo.Context) error { - return receiver.accountController.CreateAccount(ctx) + return receiver.AccountController.CreateAccount(ctx) } -func (receiver *API) DeleteDirectAccount(ctx echo.Context, accountId string) error { - return receiver.accountController.RemoveDirectAccount(ctx, accountId) +func (receiver *API) DeleteDirectAccount(ctx echo.Context, userID string) error { + return receiver.AccountController.RemoveDirectAccount(ctx, userID) } -func (receiver *API) GetDirectAccount(ctx echo.Context, accountId string) error { - return receiver.accountController.GetDirectAccount(ctx, accountId) +func (receiver *API) GetDirectAccount(ctx echo.Context, userID string) error { + return receiver.AccountController.GetDirectAccount(ctx, userID) } func (receiver *API) PaginationAccounts(ctx echo.Context, params PaginationAccountsParams) error { - return receiver.accountController.GetAccounts(ctx, params) + return receiver.AccountController.GetAccounts(ctx, params) } func (receiver *API) RemoveFromCart(ctx echo.Context, params RemoveFromCartParams) error { diff --git a/internal/swagger/middleware.go b/internal/swagger/middleware.go new file mode 100644 index 0000000..b049a4c --- /dev/null +++ b/internal/swagger/middleware.go @@ -0,0 +1,20 @@ +package swagger + +import ( + "github.com/deepmap/oapi-codegen/pkg/middleware" + "github.com/getkin/kin-openapi/openapi3" + "github.com/getkin/kin-openapi/openapi3filter" + "github.com/labstack/echo/v4" +) + +func CreateMiddleware(swagger *openapi3.T, authenticationFunc openapi3filter.AuthenticationFunc) echo.MiddlewareFunc { + validator := middleware.OapiRequestValidatorWithOptions(swagger, + &middleware.Options{ + Options: openapi3filter.Options{ + AuthenticationFunc: authenticationFunc, + }, + }, + ) + + return validator +} diff --git a/internal/utils/authenticator.go b/internal/utils/authenticator.go index db3f5f7..9151c93 100644 --- a/internal/utils/authenticator.go +++ b/internal/utils/authenticator.go @@ -16,19 +16,22 @@ const ( prefix = "Bearer " ) -func NewAuthenticator(jwtUtil *JWT[models.AuthJWTDecoded]) openapi3filter.AuthenticationFunc { +func NewAuthenticator(jwtUtil *JWT) openapi3filter.AuthenticationFunc { return func(ctx context.Context, input *openapi3filter.AuthenticationInput) error { if jwtUtil == nil { - return errors.New(fmt.Errorf("jwt util is nil"), errors.ErrInvalidArgs).Extract() + return errors.New( + fmt.Errorf("jwt util is nil: %w", errors.ErrInvalidArgs), + errors.ErrInvalidArgs, + ).Extract() } - return authenticate(jwtUtil, ctx, input) + return authenticate(ctx, jwtUtil, input) } } -func authenticate(jwtUtil *JWT[models.AuthJWTDecoded], ctx context.Context, input *openapi3filter.AuthenticationInput) error { - if input.SecuritySchemeName != "BearerAuth" { - return fmt.Errorf("security scheme %s != 'BearerAuth'", input.SecuritySchemeName) +func authenticate(ctx context.Context, jwtUtil *JWT, input *openapi3filter.AuthenticationInput) error { + if input.SecuritySchemeName != "Bearer" { + return fmt.Errorf("security scheme %s != 'Bearer'", input.SecuritySchemeName) } // Now, we need to get the JWS from the request, to match the request expectations @@ -48,12 +51,12 @@ func authenticate(jwtUtil *JWT[models.AuthJWTDecoded], ctx context.Context, inpu // access the claims data we generate in here. echoCtx := middleware.GetEchoContext(ctx) - echoCtx.Set(models.AuthJWTDecodedUserIDKey, token.UserID) + echoCtx.Set(models.AuthJWTDecodedUserIDKey, token) return nil } -// extracts a JWS string from an Authorization: Bearer header +// extracts a JWS string from an Authorization: Bearer header. func parseJWSFromRequest(request *http.Request) (string, errors.Error) { header := request.Header.Get("Authorization") diff --git a/internal/utils/echo_response.go b/internal/utils/echo_response.go index 5a18d2d..9c0e0be 100644 --- a/internal/utils/echo_response.go +++ b/internal/utils/echo_response.go @@ -14,6 +14,7 @@ var httpStatuses = map[errors.ErrorType]int{ errors.ErrNoAccess: http.StatusForbidden, errors.ErrNotFound: http.StatusNotFound, errors.ErrMethodNotImplemented: http.StatusNotImplemented, + errors.ErrConflict: http.StatusConflict, } func DetermineEchoErrorResponse(ctx echo.Context, err errors.Error) error { diff --git a/internal/utils/jwt.go b/internal/utils/jwt.go index 92c03ab..22169b4 100644 --- a/internal/utils/jwt.go +++ b/internal/utils/jwt.go @@ -1,18 +1,15 @@ package utils import ( - "encoding/json" "errors" "fmt" - "strings" "time" "github.com/golang-jwt/jwt/v5" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" - jsonUtil "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/json" ) -type JWT[T any] struct { +type JWT struct { privateKey []byte publicKey []byte algorithm *jwt.SigningMethodRSA @@ -21,8 +18,8 @@ type JWT[T any] struct { audience string } -func NewJWT[T any](configuration *models.JWTConfiguration) *JWT[T] { - return &JWT[T]{ +func NewJWT(configuration *models.JWTConfiguration) *JWT { + return &JWT{ privateKey: []byte(configuration.PrivateKey), publicKey: []byte(configuration.PublicKey), algorithm: &configuration.Algorithm, @@ -32,38 +29,10 @@ func NewJWT[T any](configuration *models.JWTConfiguration) *JWT[T] { } } -func (receiver *JWT[T]) Create(content *T) (string, error) { - privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(receiver.privateKey) - if err != nil { - return "", fmt.Errorf("failed to parse private key on of : %w", err) - } - - encoded, err := json.Marshal(content) - if err != nil { - return "", fmt.Errorf("failed to encode content to json on of : %w", err) - } - - now := time.Now().UTC() - - claims := jwt.MapClaims{ - "dat": string(encoded), // Our custom data. - "exp": now.Add(receiver.expiresIn).Unix(), // The expiration time after which the token must be disregarded. - "aud": receiver.audience, // Audience - "iss": receiver.issuer, // Issuer - } - - token, err := jwt.NewWithClaims(receiver.algorithm, claims).SignedString(privateKey) - if err != nil { - return "", fmt.Errorf("failed to sing on of : %w", err) - } - - return token, nil -} - -func (receiver *JWT[T]) Validate(tokenString string) (*T, error) { +func (receiver *JWT) Validate(tokenString string) (string, error) { key, err := jwt.ParseRSAPublicKeyFromPEM(receiver.publicKey) if err != nil { - return nil, fmt.Errorf("failed to parse rsa public key on of : %w", err) + return "", fmt.Errorf("failed to parse rsa public key on of : %w", err) } parseCallback := func(token *jwt.Token) (any, error) { @@ -81,23 +50,18 @@ func (receiver *JWT[T]) Validate(tokenString string) (*T, error) { jwt.WithIssuer(receiver.issuer), ) if err != nil { - return nil, fmt.Errorf("failed to parse jwt token on of : %w", err) + return "", fmt.Errorf("failed to parse jwt token on of : %w", err) } claims, ok := token.Claims.(jwt.MapClaims) if !ok || !token.Valid { - return nil, errors.New("token is invalid on of ") + return "", errors.New("token is invalid on of ") } - data, ok := claims["dat"].(string) + data, ok := claims["id"].(string) if !ok { - return nil, errors.New("data is empty or not a string on of ") + return "", errors.New("data is empty or not a string on of ") } - parsedData, err := jsonUtil.Parse[T](strings.NewReader(data)) - if err != nil { - return nil, fmt.Errorf("failed to parse data on of : %w", err) - } - - return parsedData, nil + return data, nil } diff --git a/internal/utils/jwt_test.go b/internal/utils/jwt_test.go deleted file mode 100644 index 797e56c..0000000 --- a/internal/utils/jwt_test.go +++ /dev/null @@ -1,129 +0,0 @@ -package utils_test - -import ( - "strings" - "testing" - "time" - - "github.com/golang-jwt/jwt/v5" - "github.com/stretchr/testify/assert" - "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" - "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" -) - -func TestJWT(t *testing.T) { - type user struct { - Name string `json:"name"` - Age int `json:"age"` - } - - testUser := user{ - Name: "test", - Age: 80, - } - - publicKey := `-----BEGIN PUBLIC KEY----- - MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyt4XuLovUY7i12K2PIMb - QZOKn+wFFKUvxvKQDel049/+VMpHMx1FLolUKuyGp9zi6gOwjHsBPgc9oqr/eaXG - QSh7Ult7i9f+Ht563Y0er5UU9Zc5ZPSxf9O75KYD48ruGkqiFoncDqPENK4dtUa7 - w0OqlN4bwVBbmIsP8B3EDC5Dof+vtiNTSHSXPx+zifKeZGyknp+nyOHVrRDhPjOh - zQzCom0MSZA/sJYmps8QZgiPA0k4Z6jTupDymPOIwYeD2C57zSxnAv0AfC3/pZYJ - bZYH/0TszRzmy052DME3zMnhMK0ikdN4nzYqU0dkkA5kb5GtKDymspHIJ9eWbUuw - gtg8Rq/LrVBj1I3UFgs0ibio40k6gqinLKslc5Y1I5mro7J3OSEP5eO/XeDLOLlO - JjEqkrx4fviI1cL3m5L6QV905xmcoNZG1+RmOg7D7cZQUf27TXqM381jkbNdktm1 - JLTcMScxuo3vaRftnIVw70V8P8sIkaKY8S8HU1sQgE2LB9t04oog5u59htx2FHv4 - B13NEm8tt8Tv1PexpB4UVh7PIualF6SxdFBrKbraYej72wgjXVPQ0eGXtGGD57j8 - DUEzk7DK2OvIWhehlVqtiRnFdAvdBj2ynHT2/5FJ/Zpd4n5dKGJcQvy1U1qWMs+8 - M7AHfWyt2+nZ04s48+bK3yMCAwEAAQ== - -----END PUBLIC KEY-----` - - privateKey := `-----BEGIN RSA PRIVATE KEY----- - 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 - -----END RSA PRIVATE KEY-----` - - publicKey = strings.Replace(publicKey, "\t", "", -1) - privateKey = strings.Replace(privateKey, "\t", "", -1) - - jwt := utils.NewJWT[user](&models.JWTConfiguration{ - PrivateKey: privateKey, - PublicKey: publicKey, - Algorithm: *jwt.SigningMethodRS256, - ExpiresIn: 15 * time.Minute, - Issuer: "issuer1", - Audience: "audience1", - }) - - t.Run("Успешная генерация токена", func(t *testing.T) { - assert.NotPanics(t, func() { - token, err := jwt.Create(&testUser) - - assert.NoError(t, err) - assert.NotZero(t, token) - assert.NotEmpty(t, token) - }) - }) - - t.Run("Успешная валидация токена", func(t *testing.T) { - assert.NotPanics(t, func() { - token, err := jwt.Create(&testUser) - - isNoError := assert.NoError(t, err) - - if isNoError { - parsedUser, err := jwt.Validate(token) - - assert.NoError(t, err) - assert.NotNil(t, parsedUser) - assert.Equal(t, &testUser, parsedUser) - } - }) - }) -} diff --git a/internal/utils/pagination.go b/internal/utils/pagination.go index b243568..ccb63c8 100644 --- a/internal/utils/pagination.go +++ b/internal/utils/pagination.go @@ -24,7 +24,7 @@ func DeterminePagination(page, limit interface{}) *models.Pagination { limitNumber, isLimitNumberOK := limit.(int64) - if !isLimitNumberOK || limitNumber > models.DefaultLimit { + if !isLimitNumberOK || limitNumber > models.DefaultLimit || limitNumber < 1 { return models.DefaultLimit }